Como faço para remover vários caracteres especiais de um arquivo?

1

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

Existe uma maneira mais fácil de adicionar ^ I ( Ctrl+V+I )? Este é o primeiro script que escrevi para mim há cerca de 6 meses, depois de participar de uma aula de programação de 2 dias. Eu não tenho certeza se eu fiz isso por mais tempo do que o necessário, então quaisquer dicas gerais também serão apreciadas.

#!/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
    
por Emile 28.11.2017 / 22:30

4 respostas

1

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

 for c in "^I" "^M"; do
    for file; do
       if grep "$c" "$file"; then
          ...
          etc.
          ...
       fi
    done
 done
    
por 28.11.2017 / 22:38
1

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

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

Exemplo usando uma classe de caracteres para remover. Nos parênteses, o 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)
    
por 28.11.2017 / 22:52
0

Isso é certamente muito, muito mais do que precisa ser. Tudo que você precisa é o tr utility , além de um loop e redirecionamentos para agir os 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 , tr remove os caracteres especificados. Os caracteres a serem removidos são passados juntos como o primeiro argumento não opcional. Você pode usar escapes de barra invertida para representar caracteres especiais: \n para uma nova linha (^ J), \r para um retorno de carro (^ M), \t para uma guia (^ I), etc.

Eu não reproduzi o código para perguntar ao usuário porque é inútil. Os diretórios causarão um erro com o redirecionamento de qualquer maneira, e é realmente o trabalho do chamador não solicitar uma ação sem sentido, como tratar um diretório como um arquivo regular, então eu 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 usando mktemp para que o script seja robusto. Ele funcionará desde que você tenha permissão de gravação no diretório que contém o arquivo, sem correr o risco de sobrescrever um arquivo existente. É seguro mesmo que esse diretório seja gravável por outros usuários que possam tentar injetar outros dados (um possível problema em /tmp ).

O comando mv é chamado apenas se a chamada para tr for bem-sucedida, portanto não há risco de perder dados se tr falhar, por exemplo, porque o disco fica cheio no meio.

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

  • Você pode verificar os caracteres especiais primeiro. Há várias maneiras para se fazer isso. Uma maneira é remover tudo exceto aqueles caracteres especiais e contar o número de caracteres resultantes. Como uma otimização, canalize head -c 1 para 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 verificar se é idêntica ao original. Isso pode ser mais lento se os arquivos já estiverem no estado desejado. Por outro lado, esta técnica generaliza 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
    
por 28.11.2017 / 23:04
0

Você já pensou em usar

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

Por exemplo, livre-se de qualquer caractere não imprimível e coloque-o em outro arquivo:

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

Modifique a lista de caracteres para se adequar ao seu aplicativo ... veja a página tr man para mais informações.

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

 echo '
 tr -d .....<characterlist>....
1
 cat filename | tr -cd '[:print:]' >/tmp/x.out
2
 echo '%pre%1%pre%2%pre%3%pre%4' | tr -d '[%pre%1-%pre%3]' | od -c
3%pre%4' | tr -d '[%pre%1-%pre%3]' | od -c
    
por 29.11.2017 / 01:28