El siguiente script elimina actualmente el carácter ^M ( Ctrl+V+M
). Siento que es un poco largo pero también necesito agregar ^I y cualquier otro personaje que pueda ver en el futuro.
¿Existe una manera más sencilla de agregar ^I ( Ctrl+V+I
)? Este es el primer script que escribí hace unos 6 meses después de asistir a una clase de programación de shell de 2 días. No estoy seguro de haberlo hecho más largo de lo necesario, por lo que también agradecería cualquier consejo general.
#!/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
Respuesta1
Sin duda, esto es mucho, mucho más largo de lo necesario. Todo lo que necesitas es eltr
utilidad, además de un bucle y redirecciones para actuar sobre los archivos que se pasan como argumentos al script.
#!/bin/sh
for file do
tr -d '\r\t' <"$file" >"$file.safe"
done
Con la opción -d
, tr
elimina los caracteres especificados. Los caracteres que se van a eliminar se pasan juntos como el primer argumento que no es de opción. Puede utilizar barras invertidas para representar caracteres especiales: \n
para una nueva línea (^J), \r
para un retorno de carro (^M), \t
para una tabulación (^I), etc.
No he reproducido el código para preguntarle al usuario porque no tiene sentido. Los directorios causarán un error con la redirección de todos modos, y realmente es trabajo de la persona que llama no solicitar una acción sin sentido, como tratar un directorio como un archivo normal, por lo que también me salté esa parte.
Si desea reemplazar el archivo original, escriba en un archivo temporal y luego mueva el resultado a su lugar.
#!/bin/sh
for file do
tmp="$(TMPDIR=$(dirname -- "$file") mktemp)"
tr -d '\r\t' <"$file" >"$tmp" && mv -f -- "$tmp" "$file"
done
El nombre del archivo temporal se construye utilizando mktemp
para que el script sea robusto. Funcionará siempre que tenga permiso de escritura en el directorio que contiene el archivo, sin correr el riesgo de sobrescribir un archivo existente. Es seguro incluso si otros usuarios pueden escribir en ese directorio y podrían intentar inyectar otros datos (un problema potencial en /tmp
).
El mv
comando sólo se invoca si la llamada tuvo tr
éxito, por lo que no hay riesgo de perder datos si tr
falla, por ejemplo, porque el disco se llena a mitad de camino.
Si desea evitar reemplazar el archivo por un archivo nuevo e idéntico si no contiene ningún carácter especial, hay dos maneras:
Puede comprobar primero los caracteres especiales. Hay varias formas de hacerlo. Una forma es eliminar todo excepto los caracteres especiales y contar el número de caracteres resultantes. Como optimización, canalice
head -c 1
para que no necesite revisar todo el archivo si se encuentra un carácter especial cerca de la parte superior: de esa manera el recuento es 0 si no hay nada que hacer y 1 en caso contrario.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
Puedes hacer la transformación y luego comprobar si es idéntica al original. Esto puede ser más lento si los archivos ya se encuentran en el estado deseado. Por otro lado, esta técnica se generaliza a casos en los que no es fácil determinar si el archivo se encuentra en el estado deseado.
tr -d '\r\t' <"$file" >"$tmp" && if cmp -s "$tmp" "$file"; then rm -- "$tmp" else mv -f -- "$tmp" "$file" fi
Respuesta2
Puedes poner un bucle alrededor de tu script. Entonces:
for c in "^I" "^M"; do
for file; do
if grep "$c" "$file"; then
...
etc.
...
fi
done
done
Respuesta3
Prefiero este Perl One Liner. El '\cM' es el carácter de control-M. Se realizará una copia de seguridad de los archivos originales con la extensión '.bak'. Esta extensión puede ser su elección.
perl -i.bak -pe 's/\cM//g;' file(s)
Ejemplo usando una clase de caracteres para eliminar. Entre paréntesis, Perl encontrará control-I y control-M y los eliminará. Aunque no lo he probado exactamente.
perl -i.bak -pe 's/[\cM\cI]//g;' files(s)
Respuesta4
¿Has pensado en utilizar
tr -d .....<characterlist>....
Por ejemplo, elimine los caracteres no imprimibles y colóquelos en otro archivo:
cat filename | tr -cd '[:print:]' >/tmp/x.out
Modifique la lista de caracteres para adaptarla a su aplicación... consulte la tr
página de manual para obtener más información.
También es bueno porque se permiten rangos de expresiones regulares:
echo '\001\002\003\004' | tr -d '[\001-\003]' | od -c