Como removo vários caracteres especiais de um arquivo?

Como removo vários caracteres especiais de um arquivo?

O script abaixo atualmente remove o caractere ^M ( Ctrl+V+M). Acho que é um pouco prolixo, mas também preciso adicionar ^I e quaisquer outros personagens que possa ver no futuro.

Existe uma maneira mais fácil de adicionar ^I ( Ctrl+V+I)? Este é o primeiro script que escrevi para mim mesmo há cerca de 6 meses, depois de participar de uma aula de programação shell de 2 dias. Não tenho certeza se demorei mais do que o necessário, então qualquer dica geral também seria apreciada.

#!/bin/bash  

    echo "$# item(s) to review."
    question='Do you want to remove the ^M characters?'

    for file
    do
            if grep "^M" "$file" >> /dev/null 2> /dev/null
            then
                    echo "$file contains special characters"
                    echo $question
                    read answer
                            if    [[ "$answer" == [yY] ]]
                            then
                                    cat "$file" | sed "s/^M//" > "$file.safe"
                                    echo "Special characters have been removed and $file.safe has been created."
                            elif  [[ "$answer" == [yY][eE][sSaA]* ]]
                            then
                                    cat "$file" | sed "s/^M//" > "$file.safe"
                                    echo "Special characters have been removed and $file.safe has been created."
                            else
                                    echo "Special characters have NOT been removed."
                            fi
            elif [[ -d $file ]]
            then
                    echo "$file is a directory"
            else
                    echo "No special characters in $file"
            fi
    done

Responder1

Isto é certamente muito, muito mais longo do que o necessário. Tudo que você precisa é otrUtilitário, além de um loop e redirecionamentos para atuar nos arquivos que são passados ​​como argumentos para o script.

#!/bin/sh
for file do
  tr -d '\r\t' <"$file" >"$file.safe"
done

Com a opção -d, trremove os caracteres especificados. Os caracteres a serem removidos são passados ​​juntos como o primeiro argumento sem opção. Você pode usar escapes de barra invertida para representar caracteres especiais: \npara uma nova linha (^J), \rpara um retorno de carro (^M), \tpara uma tabulação (^I), etc.

Não reproduzi o código para perguntar ao usuário porque é inútil. Os diretórios causarão um erro de redirecionamento de qualquer maneira, e é realmente função do chamador não solicitar uma ação absurda, como tratar um diretório como um arquivo normal, então também pulei essa parte.

Se você deseja substituir o arquivo original, grave em um arquivo temporário e mova o resultado para o lugar.

#!/bin/sh
for file do
  tmp="$(TMPDIR=$(dirname -- "$file") mktemp)"
  tr -d '\r\t' <"$file" >"$tmp" && mv -f -- "$tmp" "$file"
done

O nome do arquivo temporário é construído mktemppara que o script seja robusto. Funcionará desde que você tenha permissão de gravação no diretório que contém o arquivo, sem correr o risco de substituir um arquivo existente. É seguro mesmo que esse diretório possa ser gravado por outros usuários que possam tentar injetar outros dados (um problema potencial no /tmp).

O mvcomando só é invocado se a chamada for trbem-sucedida, portanto não há risco de perda de dados se trfalhar, por exemplo, porque o disco fica cheio no meio do caminho.

Se você quiser evitar a substituição do arquivo por um arquivo novo e idêntico, se ele não contiver nenhum caractere especial, há duas maneiras:

  • Você pode verificar os caracteres especiais primeiro. Há várias maneiras de fazer isso. Uma maneira é remover tudo, exceto os caracteres especiais, e contar o número de caracteres resultantes. Como otimização, faça um canal head -c 1para que você não precise percorrer todo o arquivo se um caractere especial for encontrado próximo ao topo: dessa forma, a contagem será 0 se não houver nada para fazer e 1 caso contrário.

    if [ "$(tr -dc '\r\t' <"$file" | head -c 1 | wc -c)" -ne 0 ]; then
      tr -d '\r\t' <"$file" >"$tmp" && mv -f -- "$tmp" "$file"
    fi
    
  • Você pode fazer a transformação e depois verificar se é idêntico ao original. Isso pode ser mais lento se os arquivos já estiverem no estado desejado. Por outro lado, esta técnica é generalizada para casos em que não é fácil determinar se o arquivo está no estado desejado.

    tr -d '\r\t' <"$file" >"$tmp" &&
    if cmp -s "$tmp" "$file"; then
      rm -- "$tmp"
    else
      mv -f -- "$tmp" "$file"
    fi
    

Responder2

Você pode colocar um loop em seu script. Então:

 for c in "^I" "^M"; do
    for file; do
       if grep "$c" "$file"; then
          ...
          etc.
          ...
       fi
    done
 done

Responder3

Eu prefiro este perl one liner. O '\cM' é o caractere control-M. Será feito backup do(s) arquivo(s) original(ais) com a extensão '.bak'. Esta extensão pode ser sua escolha.

perl -i.bak -pe 's/\cM//g;'  file(s)

Exemplo usando uma classe de caracteres para remover. Entre colchetes, perl encontrará control-I e control-M e os removerá. Eu não testei isso exatamente.

perl -i.bak -pe 's/[\cM\cI]//g;' files(s)

Responder4

Você já pensou em usar

 tr -d .....<characterlist>....

Por exemplo, livre-se de quaisquer caracteres não imprimíveis e coloque-os em outro arquivo:

 cat filename | tr -cd '[:print:]' >/tmp/x.out

Modifique a lista de caracteres para se adequar à sua aplicação... consulte a trpágina de manual para obter mais informações.

Também é bom porque os intervalos de regex são permitidos:

 echo '\001\002\003\004' | tr -d '[\001-\003]' | od -c

informação relacionada