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 é otr
Utilitá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
, tr
remove 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: \n
para uma nova linha (^J), \r
para um retorno de carro (^M), \t
para 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 mktemp
para 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 mv
comando só é invocado se a chamada for tr
bem-sucedida, portanto não há risco de perda de dados se tr
falhar, 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 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 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 tr
pá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