renomear em massa (ou exibir corretamente) arquivos com caracteres especiais

20

Eu tenho um monte de diretórios e subdiretórios que contêm arquivos com caracteres especiais, como este arquivo:

robbie@phil:~$ ls testsktest.txt 
test?sktest.txt

A descoberta revela uma sequência de escape:

robbie@phil:~$ find testsktest.txt -ls 
424512 4000 -rwxr--r-x   1 robbie   robbie    4091743 Jan 26 00:34 test\323sktest.txt

A única razão pela qual posso digitar seus nomes no console é por causa do preenchimento de guias. Isso também significa que eu posso renomeá-los manualmente (e remover o caractere especial).

Eu configurei LC_ALL para UTF-8, o que parece não ajudar (também não em um novo shell):

robbie@phil:~$ echo $LC_ALL
en_US.UTF-8

Estou conectando à máquina usando ssh do meu mac. É uma instalação do Ubuntu:

robbie@phil:~$ cat /etc/lsb-release 
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=7.10
DISTRIB_CODENAME=gutsy
DISTRIB_DESCRIPTION="Ubuntu 7.10"

Shell é Bash, TERM está definido como xterm-color.

Esses arquivos já estão lá há um bom tempo e não foram criados usando essa instalação do Ubuntu. Portanto, não sei quais eram as configurações de codificação do sistema.

Eu tentei as coisas ao longo das linhas de:

find . -type f -ls | sed 's/[^a-zA-Z0-9]//g'

Mas não consigo encontrar uma solução que faça tudo o que eu quero:

  1. Identifique todos os arquivos que possuem caracteres não exibidos (o item acima ignora demais)
  2. Para todos esses arquivos em uma árvore de diretórios (recursivamente), execute mv oldname newname
  3. Opcionalmente, a capacidade de transliterar caracteres especiais como ä para a (não necessário, mas seria incrível)

OU

  1. Exiba corretamente todos esses arquivos (e nenhum erro nos aplicativos ao tentar abri-los)

Eu tenho bits e partes, como iterar sobre todos os arquivos e movê-los, mas identificar os arquivos e formatá-los corretamente para o comando mv parece ser a parte mais difícil.

Qualquer informação extra sobre por que eles não são exibidos corretamente ou como "adivinhar" a codificação correta também é bem-vinda. (Eu tentei o convmv, mas parece não fazer exatamente o que eu quero: http://j3e.de/linux/convmv/ )

RobbieV
fonte
A resposta única abaixo segue a primeira maneira (encontre-as e renomeie para sua nova codificação), mas a segunda maneira também seria interessante: agora, quando você souber a codificação usada para os nomes de arquivos remotos, como ssh para o host remoto em tais de uma maneira que os nomes de arquivos sejam exibidos corretamente (e possam ser gerenciados digitando seus nomes com o teclado)?
IMZ - Ivan Zakharyaschev

Respostas:

21

Eu acho que você vê esse caractere inválido porque o nome contém uma sequência de bytes que não é válida UTF-8. Os nomes de arquivos em sistemas de arquivos unix típicos (incluindo o seu) são cadeias de bytes, e cabe aos aplicativos decidir qual codificação usar. Atualmente, existe uma tendência para usar o UTF-8, mas não é universal, especialmente em locais que nunca poderiam viver com ASCII simples e usavam outras codificações desde antes da existência do UTF-8.

Tente LC_CTYPE=en_US.iso88591 lsver se o nome do arquivo faz sentido na ISO-8859-1 (latin-1). Caso contrário, tente outros locais. Observe que apenas a LC_CTYPEconfiguração de localidade é importante aqui.

Em um código de idioma UTF-8, o comando a seguir mostra todos os arquivos cujo nome não é UTF-8 válido:

grep-invalid-utf8 () {
  perl -l -ne '/^([\000-\177]|[\300-\337][\200-\277]|[\340-\357][\200-\277]{2}|[\360-\367][\200-\277]{3}|[\370-\373][\200-\277]{4}|[\374-\375][\200-\277]{5})*$/ or print'
}
find | grep-invalid-utf8

Você pode verificar se eles fazem mais sentido em outro local com recode ou iconv :

find | grep-invalid-utf8 | recode latin1..utf8
find | grep-invalid-utf8 | iconv -f latin1 -t utf8

Depois de determinar que vários nomes de arquivos estão em uma determinada codificação (por exemplo, latin1), uma maneira de renomeá-los é

find | grep-invalid-utf8 |
rename 'BEGIN {binmode STDIN, ":encoding(latin1)"; use Encode;}
        $_=encode("utf8", $_)'

Isso usa o comando perl rename disponível no Debian e Ubuntu. Você pode passá-lo -npara mostrar o que faria sem realmente renomear os arquivos.

Gilles 'SO- parar de ser mau'
fonte
Obrigado, vou tentar algumas dessas coisas hoje mais tarde! Parece que este será a resposta aceite :)
RobbieV
A descoberta | O comando grep '[[: print:]]' parece simplesmente retornar todos os arquivos. O UTF-8 não deve ser compatível com muitas outras codificações com caracteres "normais"?
precisa saber é o seguinte
@RobbieV: Eu digitei e pretendia grep [^[:print:]]procurar caracteres não imprimíveis. Mas acabei de testar com o GNU grep e as sequências UTF-8 inválidas não são capturadas [^[:print:]](o que faz sentido, pois não são caracteres imprimíveis, nem caracteres). Eu editei minha postagem com uma maneira mais longa de grepping linhas com seqüências utf8 inválidas. Observe que também fixei a direção dos exemplos recodee iconv.
Gilles 'SO- stop be evil'
Isso funcionou perfeitamente. Tentei todos os comandos, exceto o iconv one, e todos eles funcionam como esperado. Pura magia!
precisa saber é o seguinte
Mesmo a codificação latin1 sugerido foi o correto :)
RobbieV
1

Sei que essa é uma pergunta antiga, mas tenho procurado a noite toda por uma solução semelhante. Encontrei algumas dicas úteis, mas elas não fizeram exatamente o que eu precisava, então tive que misturar e combinar algumas para obter o resultado correto que estava procurando.

para simplesmente remover caracteres especiais e substituí-los por um ponto (.)

for f in *.txt; do mv "$f" `echo $f | sed "s/[^a-zA-Z0-9.]/./g"`; done

para usar em um cronjob eu fiz o seguinte para executar a cada minuto

*/1 * * * * cd /path/to/files/ && for f in *.txt; do mv "$f" `echo $f | sed "s/[^a-zA-Z0-9.]/./g"`; done >/dev/null 2>&1

Espero que alguém ache isso útil, pois fez o meu dia :)

Topps70
fonte
(1) Para maior clareza, convém mudar `…`para $(…)- veja isto , isto e isto . (2) Você deve sempre citar as referências de variáveis ​​do shell (por exemplo, "$f") a menos que tenha um bom motivo para não fazê-lo e tenha certeza de que sabe o que está fazendo. Isso se aplica mesmo a echo "$f" | sed …. Também se aplica a toda a expressão $(…)(ou `…`); ou seja, mv "$f" "$(echo "$f" | sed "…")". ... (continua)
Scott
(Continua)… (3) Você deve dizer para se proteger contra nomes de arquivos começando com . (4) Se você tiver arquivos denominados "foo ♥ bar.txt" e "foo ♠ bar.txt", isso (tentará) renomear os dois para "foo.bar.txt", possivelmente causando todos, exceto um dos arquivos a serem destruídos. (5) Por que diabos você gostaria de fazer isso uma vez a cada minuto? mv -- "$f" …-
27516 Scott
Eu tenho um script de torrent que baixa automaticamente arquivos. e, às vezes, alguns arquivos possuem caracteres que fazem com que o remetente seja desligado. simplesmente renomeando arquivos com caracteres especiais, meu cron corrigiu todos os meus problemas e o remetente faz seu trabalho sem problemas.
precisa saber é o seguinte
Então, (esse arquivo que foi - down_loaded.ext) se transforma em (this.fi.le.tha.t.was.down.loaded.ext)
Topps70
0

Agora, quando você souber qual codificação é usada para os nomes de arquivos na extremidade remota ("latin1" - de acordo com os comentários da primeira resposta), você também poderá seguir o segundo caminho - execute um termninal local e ssh dessa maneira maneira como os nomes de arquivos remotos são exibidos corretamente (em vez da primeira maneira: renomeie-os) .

Como eu , você poderia iniciar um terminal localmente que funcionaria nessa codificação especial, talvez, assim:

LC_ALL = pt_BR.latin1 xvt &

xvt representa o seu programa de terminal.

Talvez o local existente seja chamado en_US.iso88591, e não en_US.latin1, como eu assumi.

imz - Ivan Zakharyaschev
fonte
0

Isso não atende aos requisitos em massa, mas acabei de ter um problema semelhante em que havia várias versões de um arquivo com nomes semelhantes que diferiam apenas por um único caractere estranho. Infelizmente, isso significava que eu não podia renomear os infratores usando o truque curinga que costumo usar.

No final, usei o Filezilla para conectar-se como um cliente SFTP, naveguei nos arquivos e os renomeei usando a GUI. O Filezilla lidou muito bem com os caracteres desonestos.

kabadisha
fonte