¿Cómo elimino varios caracteres especiales de un archivo?

¿Cómo elimino varios caracteres especiales de un archivo?

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 eltrutilidad, 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, trelimina 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: \npara una nueva línea (^J), \rpara un retorno de carro (^M), \tpara 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 mktemppara 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 mvcomando sólo se invoca si la llamada tuvo tréxito, por lo que no hay riesgo de perder datos si trfalla, 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 1para 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 trpá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

información relacionada