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
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
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
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)
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
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