GNU/Linux >> Tutoriales Linux >  >> Linux

¿Por qué Printf es mejor que Echo?

Escuché que printf es mejor que echo . Solo puedo recordar una instancia de mi experiencia en la que tuve que usar printf porque echo no funcionó para introducir texto en algún programa en RHEL 5.8 pero printf hizo. Pero aparentemente, hay otras diferencias, y me gustaría preguntar cuáles son, así como si hay casos específicos en los que usar uno u otro.

Respuesta aceptada:

Básicamente, es un problema de portabilidad (y confiabilidad).

Inicialmente, echo no aceptó ninguna opción y no amplió nada. Todo lo que estaba haciendo era generar sus argumentos separados por un carácter de espacio y terminados por un carácter de nueva línea.

Ahora, alguien pensó que sería bueno si pudiéramos hacer cosas como echo "nt" para generar caracteres de nueva línea o de tabulación, o tener la opción de no generar el carácter de nueva línea final.

Luego pensaron más, pero en lugar de agregar esa funcionalidad al shell (como perl donde dentro de comillas dobles, t en realidad significa un carácter de tabulación), lo agregaron a echo .

David Korn se dio cuenta del error e introdujo una nueva forma de comillas de shell:$'...' que luego fue copiado por bash y zsh pero ya era demasiado tarde para ese momento.

Ahora, cuando un estándar UNIX echo recibe un argumento que contiene los dos caracteres y t , en lugar de generarlos, genera un carácter de tabulación. Y tan pronto como vea c en un argumento, deja de generar (por lo que tampoco se genera la nueva línea final).

Otros proveedores/versiones de shells/Unix eligieron hacerlo de manera diferente:agregaron un -e opción para expandir las secuencias de escape y un -n opción para no generar la nueva línea final. Algunos tienen un -E para deshabilitar las secuencias de escape, algunas tienen -n pero no -e , la lista de secuencias de escape compatibles con un echo la implementación no es necesariamente la misma que la admitida por otra.

Sven Mascheck tiene una buena página que muestra el alcance del problema.

En esos echo implementaciones que admiten opciones, generalmente no hay soporte para un -- para marcar el final de las opciones (el echo integrado de algunos shells que no son de tipo Bourne, y zsh admite - aunque para eso), por ejemplo, es difícil generar "-n" con echo en muchas conchas.

En algunos shells como bash ¹ o ksh93 ² o yash ($ECHO_STYLE variable), el comportamiento incluso depende de cómo se compiló el shell o del entorno (GNU echo El comportamiento también cambiará si $POSIXLY_CORRECT está en el entorno y con la versión, zsh ‘s con su bsd_echo opción, algunos basados ​​en pdksh con su posix opción o si se llaman como sh o no). Así que dos bash echo s, incluso de la misma versión de bash no se garantiza que se comporten de la misma manera.

POSIX dice:si el primer argumento es -n o cualquier argumento contiene barras invertidas, entonces el comportamiento no se especifica . bash echo en ese sentido no es POSIX en que, por ejemplo, echo -e no genera -e<newline> como requiere POSIX. La especificación UNIX es más estricta, prohíbe -n y requiere la expansión de algunas secuencias de escape, incluida la c uno para detener la salida.

Esas especificaciones realmente no vienen al rescate aquí dado que muchas implementaciones no cumplen. Incluso algunos certificados los sistemas como macOS no son compatibles.

Para representar realmente la realidad actual, POSIX debería decir:si el primer argumento coincide con ^-([eEn]*|-help|-version)$ expresiones regulares extendidas o cualquier argumento que contenga barras invertidas (o caracteres cuya codificación contenga la codificación del carácter de barra invertida como α en entornos locales que utilizan el conjunto de caracteres BIG5), el comportamiento no se especifica.

Considerándolo todo, no sabes qué echo "$var" se generará a menos que pueda asegurarse de que $var no contiene caracteres de barra invertida y no comienza con - . La especificación POSIX en realidad nos dice que usemos printf en cambio en ese caso.

Entonces, lo que eso significa es que no puedes usar echo para mostrar datos no controlados. En otras palabras, si está escribiendo un script y recibe información externa (del usuario como argumentos o nombres de archivos del sistema de archivos...), no puede usar echo para mostrarlo.

Esto está bien:

echo >&2 Invalid file.

Esto no es:

echo >&2 "Invalid file: $file"

(Aunque funcionará bien con algunos echo (no compatibles con UNIX) implementaciones como bash es cuando el xpg_echo la opción no se ha habilitado de una forma u otra, como en el momento de la compilación o a través del entorno).

Relacionado:¿Procesar cada línea de salida de `ping` inmediatamente en la canalización?

file=$(echo "$var" | tr ' ' _) no está bien en la mayoría de las implementaciones (las excepciones son yash con ECHO_STYLE=raw (con la salvedad de que yash Las variables no pueden contener secuencias arbitrarias de bytes, por lo que no son nombres de archivo arbitrarios) y zsh ‘s echo -E - "$var" ).

printf , por otro lado, es más confiable, al menos cuando se limita al uso básico de echo .

printf '%sn' "$var"

Mostrará el contenido de $var seguido de un carácter de nueva línea independientemente del carácter que pueda contener.

printf '%s' "$var"

Lo generará sin el carácter de nueva línea final.

Ahora, también hay diferencias entre printf implementaciones. Hay un núcleo de funciones que POSIX especifica, pero luego hay muchas extensiones. Por ejemplo, algunos admiten un %q para citar los argumentos, pero la forma en que se hace varía de shell a shell, algunos admiten uxxxx para caracteres Unicode. El comportamiento varía para printf '%10sn' "$var" en entornos locales de varios bytes, hay al menos tres resultados diferentes para printf %b '123'

Pero al final, si te apegas al conjunto de funciones POSIX de printf y no intentes hacer nada demasiado elegante con él, estás fuera de problemas.

Pero recuerda que el primer argumento es el formato, por lo que no debe contener datos variables/no controlados.

Un echo más fiable se puede implementar usando printf , como:

echo() ( # subshell for local scope for $IFS
  IFS=" " # needed for "$*"
  printf '%sn' "$*"
)

echo_n() (
  IFS=" "
  printf %s "$*"
)

echo_e() (
  IFS=" "
  printf '%bn' "$*"
)

El subshell (que implica generar un proceso adicional en la mayoría de las implementaciones de shell) se puede evitar usando local IFS con muchas conchas, o escribiéndolo como:

echo() {
  if [ "$#" -gt 0 ]; then
     printf %s "$1"
     shift
     if [ "$#" -gt 0 ]; then
       printf ' %s' "[email protected]"
     fi
  fi
  printf 'n'
}

Notas

1. cómo bash 's echo el comportamiento puede ser alterado.

Con bash , en tiempo de ejecución, hay dos cosas que controlan el comportamiento de echo (junto a enable -n echo o redefiniendo echo como función o alias):
el xpg_echo bash opción y si bash está en modo posix. posix el modo se puede habilitar si bash se llama como sh o si POSIXLY_CORRECT está en el entorno o con el posix opción:

Comportamiento predeterminado en la mayoría de los sistemas:

$ bash -c 'echo -n "
Linux
  1. ¿Por qué la tilde (~) no se expande dentro de comillas dobles?

  2. ¿Por qué un VPS de Linux es una mejor opción que un VPS de Windows?

  3. ¿Qué hace que el alojamiento en la nube sea mejor que el VPS tradicional?

  4. Lzma Vs Bzip2:mejor compresión que bzip2 en UNIX / Linux

  5. Linux Vs Windows:por qué Linux es mejor para la programación y el desarrollo web

8 razones por las que Linux Mint es mejor que Ubuntu para principiantes de Linux

11 razones por las que Linux es mejor que Windows

Linux vs Mac:7 razones por las que Linux es una mejor opción que Mac

¿Por qué Linux es tan malo y Windows 11 mejor en todos los sentidos?

¿Por qué no se encuentra el comando sudo:bundle?

¿Por qué un enrutador de hardware funciona mejor que un enrutador Linux con mejores especificaciones (RAM y CPU)?