Renomeando arquivos com charset incorreto

Quem usa Linux por alguns anos e já experimentou várias distribuições e versões diferentes já deve ter se deparado com o fato de ver os nomes de seus arquivos completamente estranhos. Isso é culpa da mudança na codificação de caracteres no seu sistema e pode ser solucionada sem muito sofrimento.

Imagine que você acabou de mudar de distribuição e ao abrir o gerenciador de arquivos para acessar um documento teve o desprazer de ver todos os seus arquivos com nomes acentuados de forma estranha. Outra situação onde isso ocorre é quando restauramos um backup antigo, ou mesmo ao acessar uma partição FAT, ou NTFS com arquivos salvos pelo Windows.

Algumas vezes poderão aparecer sinais de “?” no lugar do caractere esperado, ou mesmo outro caractere completamente errado mas o motivo é sempre o mesmo: Quando você salvou este arquivo o seu sistema estava com uma codificação diferente da atual.

Como não há uma ferramenta gráfica para tal modificação teremos que fazer este procedimento a partir de um terminal.

Descobrindo a codificação que estava o seu sistema

A primeira coisa a fazer é descobrir qual é a codificação que o seu sistema estava e para isso o método é bem simples: A partir de um terminal, vamos entrar no diretório onde estão os arquivos e passar a saída do comando “ls” para o comando “file” de forma que este último possa analisar a codificação para gente.

$ cd /tmp/lixo
$ ls -lh
total 0
-rw-r--r-- 1 wbraga wbraga 0 2009-03-14 14:37 cora??o
-rw-r--r-- 1 wbraga wbraga 0 2009-03-14 14:37 id?ia
-rw-r--r-- 1 wbraga wbraga 0 2009-03-14 14:37 x?cara
$

Veja acima que entrei no diretório /tmp/lixo (um diretório criado meramente para este teste) e observe, também, que os meus arquivos estão sendo exibidos com “?” no lugar dos caracteres acentuados. Com os comandos “ls” e “file” podemos facilmente descobrir que codificação é esta.

$ ls|file -i -
/dev/stdin: text/plain charset=iso-8859-1
$

Perceba pela saída do comando file que este meus arquivos estão com os seus nomes na codificação iso-8859-1. Como o meu sistema está usando uma outra codificação ele não está apto a exibir os caracteres acentuados.

Descobrindo a codificação que está o sistema atualmente

Agora devemos descobrir a codificação que está o nosso sistema para que na hora de renomear os arquivos eles possam ser exibidos adequadamente. A forma mais simples de fazer isso é verificando com o comando “locale”.

$ locale
LANG=pt_BR.UTF-8
LC_CTYPE="pt_BR.UTF-8"
LC_NUMERIC="pt_BR.UTF-8"
LC_TIME="pt_BR.UTF-8"
LC_COLLATE="pt_BR.UTF-8"
LC_MONETARY="pt_BR.UTF-8"
LC_MESSAGES="pt_BR.UTF-8"
LC_PAPER="pt_BR.UTF-8"
LC_NAME="pt_BR.UTF-8"
LC_ADDRESS="pt_BR.UTF-8"
LC_TELEPHONE="pt_BR.UTF-8"
LC_MEASUREMENT="pt_BR.UTF-8"
LC_IDENTIFICATION="pt_BR.UTF-8"
LC_ALL=
$

Perceba que todas as variáveis de sistema apresentadas sugerem o UTF-8 (você pode desconsiderar o pt_BR, já que este valor apenas refere-se ao idioma).

Fazendo a mágica acontecer

Indo direto ao ponto apenas use a linha de comando a seguir, mas não deixe de ler a explicação abaixo já que me deu trabalho para escrevê-la 😉 e também porque você deverá saber como mudar os parâmetros de codificação usados para os que você encontrou  nos testes feitos acima com o ls|file e locale para que as coisas funcionem como você espera.

$ for ARQ in *; do 
mv "${ARQ}" "`echo ${ARQ}|iconv -f iso8859-1 -t utf-8`"; done
$

Este comando não vai retornar nada na sua tela, mas ao listar o conteúdo do diretório o resultado será este:

$ ls -lh
total 0
-rw-r--r-- 1 wbraga wbraga 0 2009-03-14 14:37 coração
-rw-r--r-- 1 wbraga wbraga 0 2009-03-14 14:37 idéia
-rw-r--r-- 1 wbraga wbraga 0 2009-03-14 14:37 xícara
$

Perceba que os caracteres estão corretamente acentuados. Missão cumprida, agora é só repetir o procedimento com todos os demais diretórios, se existirem.

Explicando como a mágica funciona

O trabalho sujo é todo feito pelo comando “iconv”. Perceba que ele foi executado com os parâmetros “-f iso-8859-1” e “-t utf-8“, que respectivamente correspondem a “from” (de) e “to” (para), ou seja, estou solicitando uma conversão de iso-8859-1 para utf-8. Como este comando faz parte do pacote libc6 acredito que já esteja instalado em praticamente qualquer distribuição Linux atual dispensando assim a sua instalação.

Graças aos poderosos recursos de subshell e encanamento existentes em qualquer terminal Linux, foi feito um laço “for” sobre todos os arquivos do diretório atual (*), disponibilizando o nome de cada arquivo (um por  vez) na variável “ARQ”. A cada vez que o laço é iterado esta variável receberá o nome de um daqueles arquivos e que será passado ao comando “mv” para renomear do “nome esquisito” atual para o que comando “iconv” recebeu via encanamento do comando “echo” e converteu.

Melhorias e mais do mesmo

Como esta é uma linha de comando simples ela é bem pobre de automação. Se quiser você poderá autorizar as coisas criando um script de verdade que guarde os valores dos comandos file e locale.

Outra ideia é que se faça esta verificação uma vez para cada arquivo (dentro do laço for) de forma que em caso de haver vários aquivos com codificação diferente eles sejam facilmente reconhecidos.

Esta mesma técnica pode ser usada para outros tipos de conversão. Por exemplo poderiamos converter todos os nomes para minúsculas, ou para maiúsculas etc. vale aqui a substituição do iconv pelo que for mais útil para você, mas isso é assunto para outro dia 😉

Referências

Apenas as páginas de manual do iconv e do bash que podem ser acessadas pelo terminal digitando-se “man iconv” ou “man bash”.

5 thoughts on “Renomeando arquivos com charset incorreto”

  1. E ae cara beleza?

    Vendo este seu post (que diga-se de passagem eh fantastico) gostaria de saber se é possivel com essas funcoes e comandos que vc utilizou retirar a acentuacao de todos os arquivos e diretorios… Minha duvida se deve ao fato de que temos na empresa File Servers Linux e já vem de tempos os arquivos com acentuacao, entao em alguns momentos na transicao entre windows x linux em backups principalmente dá alguns problemas nessa parte de collation, entao gostaria de eliminar todos os caracteres acentuados e deixar apenas as letras…
    Consegue me dar uma luz se eh possivel efetuar tal procedimento?

    Desde ja agradeço…
    Abracos

  2. Salve @Diego ,

    Para remover a acentuação dos nomes dos arquivos altere o parâmetro “-t utf-8” do iconv (vide texto) por “-t ascii//TRANSLIT”. Assim assim ao invés de converter para utf-8 você estará convertendo para ASCII.

    Existem outras maneiras de se chegar ao mesmo resultado, mas gosto dessa que é bem simples.
    ps. A dica veio do blog Alexandre Pinheiro (http://www.alexandrepinheiro.com/2011/05/shell-script-para-retirar-os-acentos.html)

  3. Olá comunidade! Seguinte, gostei deste comando, mas preciso que ele se aplique a todas subpastas e arquivos destas subpastas, como modificar o comando para fazer isso?

    1. Tudo bem José,

      Eu optei por um laço for para que a conversão fosse realizada diretório por diretório para assegurar que estou acompanhando qualquer possível problema (paranóia minha), mas é possível sim transpassar todas as subpastas. O segredo é substituir o laço for pelo comando find.

      A grosso modo seria algo como:
      $ find DIRETORIO -type f -exec echo mv \”{}\” \”`echo {}|iconv -f iso8859-1 -t utf-8`\” \;

      Onde DIRETORIO é o caminho onde você quer começar a varredura (por exemplo poderia ser “/home/jose”, ou “/home”, “.” etc.

      E esteja atento as “\” que servem para escapar as aspas que serão necessárias no caso de arquivos contendo espaços no nome. Dependendo do shell que você esteja usando (bash, zsh, fish etc) poderá ser necessário algum ajuste com relação a isso.

Deixe uma resposta

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *

This site uses Akismet to reduce spam. Learn how your comment data is processed.