identificar arquivos com caracteres não ASCII ou imprimíveis no nome do arquivo


24

Em um tamanho de diretório de 80 GB com aproximadamente 700.000 arquivos, existem alguns nomes de arquivos com caracteres que não estão em inglês no nome do arquivo. Além de vasculhar a lista de arquivos laboriosamente, existe:

  • Uma maneira fácil de listar ou identificar esses nomes de arquivo?
  • Uma maneira de gerar caracteres imprimíveis no idioma inglês - aqueles que não estão listados no intervalo imprimível de man ascii(para que eu possa testar se esses arquivos estão sendo identificados)?

Respostas:


32

Supondo que "estrangeiro" significa "não um caractere ASCII", você pode usar findcom um padrão para encontrar todos os arquivos que não possuem caracteres ASCII imprimíveis em seus nomes:

LC_ALL=C find . -name '*[! -~]*'

(O espaço é o primeiro caractere imprimível listado em http://www.asciitable.com/ , ~é o último.)

A dica para LC_ALL=Cé necessária (na verdade, LC_CTYPE=Ce LC_COLLATE=C), caso contrário, o intervalo de caracteres é interpretado incorretamente. Veja também a página do manual glob(7). Como as LC_ALL=Ccausas findinterpretam as strings como ASCII, ele imprime caracteres de vários bytes (como π) como pontos de interrogação. Para corrigir isso, catdirecione para algum programa (por exemplo ) ou redirecione para o arquivo.

Em vez de especificar intervalos de caracteres, [:print:]também pode ser usado para selecionar "caracteres imprimíveis". Certifique-se de definir o código de idioma C ou você terá um comportamento arbitrário (aparentemente).

Exemplo:

$ touch $(printf '\u03c0') "$(printf 'x\ty')"
$ ls -F
dir/  foo  foo.c  xrestop-0.4/  xrestop-0.4.tar.gz  π
$ find -name '*[! -~]*'       # this is broken (LC_COLLATE=en_US.UTF-8)
./x?y
./dir
./π
... (a lot more)
./foo.c
$ LC_ALL=C find . -name '*[! -~]*'
./x?y
./??
$ LC_ALL=C find . -name '*[! -~]*' | cat
./x y
./π
$ LC_ALL=C find . -name '*[![:print:]]*' | cat
./x y
./π

1
Esteja ciente de que você possui nomes de arquivos que estão usando conjuntos de caracteres externos incompatíveis com UTF-8 ou ASCII. Nesses casos, você pode ver pontos de interrogação em vez de caracteres.
Lekensteyn

1
+1, mas eu usaria em LC_ALL=Cvez LC_COLLATE=Cdisso, pois não faz muito sentido definir LC_COLLATE como C sem definir LC_CTYPEe garantir que ainda funcione mesmo quando a variável LC_ALL estiver no ambiente.
Stéphane Chazelas

Se SPCé possível imprimir , o que dizer TABe LFquais também são normalmente encontrados em arquivos de texto?
Stéphane Chazelas

1
Obrigado - foram encontrados seis arquivos, que tinham hífen longo, hífen curto e uma variante de aspas simples. Todos eles se originaram do MS Word. Não há diferença nos arquivos listados entre LC_ALL e LC_COLLATE. LC_COLLATE exibiu os caracteres não ASCII corretamente, enquanto LC_ALL exibiu ??? em vez de. Excelente resposta!
Suspeito #

1
@suspectus Atualizei por resposta com base em sugestões de Stephane. Para LC_COLLATEe LC_CTYPE, veja também a página de find(1)manual.
precisa saber é o seguinte

6

Se você traduzir cada nome de arquivo usando tr -d '[\200-\377]'e compará-lo com o nome original, os nomes de arquivos com caracteres especiais não serão os mesmos.

(O que foi dito acima assumindo que você quer dizer não ASCII com estrangeiros)


2
Isso também remove [e ]na maioria das trimplementações.
Stéphane Chazelas

Sim - ele removeu [e ]no meu sistema.
precisa saber é o seguinte

+1 - a solução encontrou todos os (seis) nomes de arquivos com símbolos não ASCII (além dos [e ]). obrigado.
precisa saber é o seguinte

3

Você pode usar trpara excluir qualquer caractere externo de um nome de arquivo e comparar o resultado com o nome do arquivo original para verificar se ele continha caracteres estrangeiros.

find . -type f > filenames
while read filename; do
      stripped="$(printf '%s\n' "$filename" | tr -d -C '[[:alnum:]][[:space:]][[:punct:]]')"
      test "$filename" = "$stripped" || printf '%s\n' "$filename"; 
done < filenames

4
que é uma boa extensão para a minha resposta, mas é muito simples, nomes de arquivo pode ter quebras de linha neles e, em seguida, o script não vai funcionar
Timo

1
Se você deseja pós-processar a findsaída, use saída / entrada terminada em NUL, como mostrado nesta resposta .
Lekensteyn

0

A resposta aceita é útil, mas se seus nomes de arquivos já estiverem na codificação especificada em LANG/ LC_CTYPE, é melhor fazer:

LC_COLLATE=C find . -name '*[! -~]*'

As classes de caracteres são afetadas LC_CTYPE, mas o comando acima não usa classes de caracteres, apenas intervalos, portanto, LC_CTYPEapenas impede que caracteres incomuns sejam substituídos por pontos de interrogação.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.