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