Removendo todos os caracteres não-ascii de um fluxo de trabalho (arquivo)

12

Como removeria todos os caracteres não-ascii de um arquivo? Haveria um comando específico para fazer isso?

grep --colour='auto' -P -n'[^\x00-\x7]' /usr/local/...

Acredito que isso encontre os caracteres no fluxo de trabalho, mas como removeria todas as instâncias dos caracteres em questão?

Mizole Ni
fonte
2
relacionado: se você apenas deseja evitar problemas com caracteres de controle (em vez de se livrar deles silenciosamente), pode simplesmente usá cat -v-los para mostrá-los na representação ASCII para eles. (por exemplo, ^Gpara \007)
Matija Nalis 15/10
1
Quando você diz "caracteres não-ascii", você também inclui caracteres acentuados?
Capitão Man
1
@MatijaNalis Mais informações sobre a representação: en.wikipedia.org/wiki/Caret_notation
wjandrea 15/18
1
Qual é o caso de uso? Muitas vezes, existem ferramentas específicas ou abordagens diferentes que funcionam muito melhor do que simplesmente remover um monte de caracteres especiais. Observe que o ASCII inclui vários caracteres "especiais", como guias verticais, campainha e NUL - você tem certeza de que não quer dizer caracteres imprimíveis ?
L0b0 15/10

Respostas:

25

Caracteres ASCII são caracteres no intervalo de 0 a 177 (octal), inclusive .

Para excluir caracteres fora deste intervalo em um arquivo, use

LC_ALL=C tr -dc '\0-\177' <file >newfile

O trcomando é um utilitário que funciona com caracteres únicos , substituindo-os por outros caracteres únicos (transliteração), excluindo-os ou compactando execuções do mesmo caractere em um único caractere.

O comando acima deve ler filee escrever o conteúdo modificado para newfile. A -dopção de trfazer o utilitário excluir caracteres (em vez de transliterá-los) e -cconsiderar caracteres fora do intervalo especificado (em vez de dentro).

LC_ALL=Cgarante que cada valor de byte crie um caractere válido. Sem ele, algumas trimplementações seriam abortadas se encontrassem sequências de bytes que não formam caracteres válidos na codificação de caracteres do código do idioma.


Para substituir o arquivo original pelo modificado, use

LC_ALL=C tr -dc '\0-\177' <file >newfile &&
mv newfile file

Isso renomeia o novo arquivo para o nome do arquivo antigo após a trconclusão com êxito. Se trnão for concluído com êxito, porque ele não pôde ler o arquivo original ou não gravou no novo arquivo, o arquivo original permanecerá inalterado.

Como alternativa, para preservar o máximo possível dos metadados (permissões etc.) do arquivo original, use

cp file tmpfile &&
LC_ALL=C tr -dc '\0-\177' <tmpfile >file &&
rm tmpfile
Kusalananda
fonte
14

Com perl

perl -pi -e 's/[^[:ascii:]]//g'
Isaac
fonte
9

Se tudo o que você precisa é de uma regex: [\x00-\x7F]você pode aplicar a vários utilitários:

<file LC_ALL=C   sed   's/[^\o0-\o177]//g'      # GNU sed without POSIXLY_CORRECT
<file LC_ALL=C   awk   '{gsub(/[^\0-\177]/,"");print}'
<file            perl  -pe 's/[^[:ascii:]]//g;'
<file LC_ALL=C   tr    -dc '\0-\177'

Entenda que sed, awk e perl esperam "arquivos de texto", conforme definido no Unix. Tudo funciona bem neste caso. Mas, especificamente, o awk adiciona uma nova linha à direita (se ela existe no arquivo de origem ou não) (usar printf remove TODAS as novas linhas na entrada). O tr foi projetado para funcionar com qualquer tipo de arquivo. No entanto, o NUL ( \0) não é um caractere válido em um arquivo de texto POSIX e deve ser evitado:

As linhas não contêm caracteres NUL ...

De fato, muitos caracteres de controle gerariam outros problemas sob algumas condições específicas.
Então, provavelmente você precisa[\x07-\x0d\x20-\x7e]

<file LC_ALL=C   sed   's/[^\o007-\o015\o040-\o176]//g'            # GNU sed without POSIXLY_CORRECT
<file LC_ALL=C   awk   '{gsub(/[^\0-\15\40-\176]/,"");print}'
<file            perl  -pe 's/[^\x{7}-\x{d}\x{20}-\x{7e}]//g;'
<file LC_ALL=C   tr    -dc '\7-\15\40-\176'

O intervalo de 7-13 (em decimal) é \a\b\t\n\v\f\r(em ordem).
Um intervalo semelhante (provavelmente mais portátil) pode ser escrito como [^[:space:][:print:]] (similar because it doesn't include\ a \ b` --bell e backspace--).

<file LC_ALL=C   sed   's/[^[:space:][:print:]]//g'  # GNU sed without POSIXLY_CORRECT
<file LC_ALL=C   awk   '{gsub(/[^[:space:][:print:]]/,"");print}'
<file            perl   -pe 's/[^[:space:][:print:]]//g;'
<file LC_ALL=C   tr     -dc '[:space:][:print:]'

Relacionado:
Regex qualquer
solução Perl de caractere ASCII
Arquivo de texto Posix

Isaac
fonte
Observe que a entrada para trpode ser qualquer tipo de arquivo, não apenas arquivos de texto. awkpor outro lado, pega um arquivo de texto.
Kusalananda
É muito difícil para mim encontrar outra coisa para chamar um arquivo de "apenas caracteres ascii", exceto um "arquivo de texto" (sim, sim: em termos leigos). @Kusalananda (observação sobre o awk adicionada de qualquer maneira).
Isaac
Observe que gensub()é uma extensão gawk. Você gostaria gsub(...); printe usaria sequências octal em vez de hexadecimal (e LC_ALL = C) para ser (mais) portátil.
Stéphane Chazelas 31/07/19
@ StéphaneChazelas Qual é a limitação do GNU sed que torna a sintaxe específica do GNU (eu entendo o problema POSIXLY_CORRECT).
Isaac
[^\o0]deve corresponder a caracteres diferentes da barra invertida, o e 0 no POSIX sed(em todas as implementações, exceto GNU sed). Isso não é uma limitação do GNU, sedmas uma extensão não compatível, e é por isso que é desativado quando POSIXLY_CORRECT está no ambiente).
Stéphane Chazelas 31/07/19