He escuchado que printf
es mejor que echo
. Solo puedo recordar una instancia de mi experiencia en la que tuve que usarla printf
porque echo
no funcionó para alimentar algún texto en algún programa en RHEL 5.8, pero lo printf
hizo. Pero al parecer, hay otras diferencias, y me gustaría preguntar cuáles son, así como si hay casos específicos de cuándo usar uno frente al otro.
text-processing
echo
printf
anfibio
fuente
fuente
echo -e
?echo -e
no funcionash
, solo enbash
(por alguna razón), y Docker, por ejemplo, usash
de forma predeterminada para susRUN
comandosecho -e
para mí funciona en Android Terminal, que es esencialmentesh
.Respuestas:
Básicamente, es un problema de portabilidad (y confiabilidad).
Inicialmente,
echo
no aceptó ninguna opción y no expandió 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 "\n\t"
generar caracteres de nueva línea o tabulación, o tener una opción para 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 las comillas dobles, en\t
realidad significa un carácter de tabulación), lo agregaronecho
.David Korn se dio cuenta del error e introdujo una nueva forma de citas de shell:
$'...'
que luego fue copiadabash
yzsh
que ya era demasiado tarde.Ahora, cuando UNIX estándar
echo
recibe un argumento que contiene los dos caracteres\
yt
, en lugar de generarlos, genera un carácter de tabulación. Y tan pronto como ve\c
en un argumento, deja de emitirse (por lo que la nueva línea final tampoco se genera).Otros shells / proveedores / versiones de Unix decidieron hacerlo de manera diferente: agregaron una
-e
opción para expandir las secuencias de escape y una-n
opción para no generar la nueva línea final. Algunos tienen-E
que deshabilitar las secuencias de escape, otros tienen,-n
pero no-e
, la lista de secuencias de escape compatibles con unaecho
implementación no es necesariamente la misma que admite otra.Sven Mascheck tiene una buena página que muestra el alcance del problema .
En esas
echo
implementaciones que soportan opciones, por lo general hay ningún apoyo de un--
para marcar el final de las opciones (laecho
orden interna de algunas conchas no Bourne-como hacer, y zsh apoya-
para que aunque), por lo que, por ejemplo, es difícil salida"-n"
conecho
en Muchas conchas.En algunos shells como
bash
¹ oksh93
² oyash
($ECHO_STYLE
variable), el comportamiento incluso depende de cómo se compiló el shell o del entorno (echo
el comportamiento de GNU también cambiará si$POSIXLY_CORRECT
está en el entorno y con la versión 4 ,zsh
con subsd_echo
opción, algunos basados en pdksh con suposix
opción o si se les llama comosh
o no). Por lo tanto, dosbash
echo
s, incluso de la misma versión debash
, no tienen el mismo comportamiento.POSIX dice: si el primer argumento es
-n
o cualquier argumento contiene barras invertidas, entonces el comportamiento no está especificado .bash
echo en ese sentido no es POSIX porque, por ejemplo,echo -e
no se emite-e<newline>
como lo 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
que detiene la salida.Esas especificaciones realmente no vienen al rescate aquí dado que muchas implementaciones no son compatibles. Incluso algunos sistemas certificados como macOS 5 no son compatibles.
Para representar realmente la realidad actual, POSIX debería decir : si el primer argumento coincide con la
^-([eEn]*|-help|-version)$
expresión regular extendida o algún argumento contiene barras diagonales inversas (o caracteres cuya codificación contiene la codificación del carácter de barra diagonal inversa comoα
en los entornos locales que utilizan el juego de caracteres BIG5), entonces el comportamiento es sin especificarEn general, no sabe qué
echo "$var"
generará a menos que pueda asegurarse de que$var
no contenga caracteres de barra invertida y no comience con-
. La especificación POSIX realmente nos dice que usemosprintf
en su lugar en ese caso.Entonces, lo que eso significa es que no se puede usar
echo
para mostrar datos no controlados. En otras palabras, si está escribiendo un script y está recibiendo información externa (del usuario como argumentos, o nombres de archivo del sistema de archivos ...), no puede usarecho
para mostrarlo.Esto esta bien:
Esto no es:
(Aunque funcionará bien con algunas
echo
implementaciones (no compatibles con UNIX) comobash
's cuando laxpg_echo
opción no se ha habilitado de una forma u otra, como en el momento de la compilación o en el entorno).file=$(echo "$var" | tr ' ' _)
no está bien en la mayoría de las implementaciones (las excepciones sonyash
conECHO_STYLE=raw
(con la advertencia de queyash
las variables no pueden contener secuencias arbitrarias de bytes, por lo que no son nombres de archivos arbitrarios) yzsh
'secho -E - "$var"
6 ).printf
, por otro lado, es más confiable, al menos cuando se limita al uso básico deecho
.Producirá el contenido
$var
seguido de un carácter de nueva línea, independientemente de qué carácter pueda contener.Lo generará sin el carácter de nueva línea final.
Ahora, también hay diferencias entre las
printf
implementaciones. POSIX especifica un núcleo de características, pero hay muchas extensiones. Por ejemplo, algunos admiten a%q
para citar los argumentos, pero la forma en que se realiza varía de un shell a otro, algunos admiten\uxxxx
caracteres Unicode. El comportamiento varíaprintf '%10s\n' "$var"
en entornos locales de varios bytes, hay al menos tres resultados diferentes paraprintf %b '\123'
Pero al final, si te apegas al conjunto de características POSIX
printf
y no intentas hacer nada demasiado elegante con él, estás fuera de problemas.Pero recuerde que el primer argumento es el formato, por lo que no debe contener datos variables / no controlados.
Se
echo
puede implementar una más confiable usandoprintf
, como:El subshell (que implica generar un proceso adicional en la mayoría de las implementaciones de shell) puede evitarse
local IFS
con muchos shells, o escribiéndolo así:Notas
1. ¿Cómo
bash
'secho
comportamiento puede ser alterado.Con
bash
, en tiempo de ejecución, hay dos cosas que controlan el comportamiento deecho
(al ladoenable -n echo
o redefinirecho
como una función o alias): laxpg_echo
bash
opción y sibash
está en modo posix.posix
El modo se puede habilitar sibash
se llama comosh
o siPOSIXLY_CORRECT
está en el entorno o con laposix
opción:Comportamiento predeterminado en la mayoría de los sistemas:
xpg_echo
expande las secuencias como UNIX requiere:Todavía honra
-n
y-e
(y-E
):Con
xpg_echo
y modo POSIX:Esta vez,
bash
es compatible con POSIX y UNIX. Tenga en cuenta que en el modo POSIX,bash
todavía no es compatible con POSIX ya que no se genera-e
en:Los valores predeterminados para xpg_echo y posix se pueden definir en el momento de la compilación con las opciones
--enable-xpg-echo-default
y--enable-strict-posix-default
para elconfigure
script. Eso suele ser lo que hacen las versiones recientes de OS / X para construir su/bin/sh
.Sin. En realidad, eso no es cierto,/bin/bash
embargo, ninguna implementación / distribución de Unix / Linux en su sano juicio lo haría normalmente/bin/bash
parece que Oracle con Solaris 11 (en un paquete opcional) está construido--enable-xpg-echo-default
(ese no fue el caso en Solaris 10).2. ¿Cómo
ksh93
'secho
comportamiento puede ser alterado.En
ksh93
, siecho
se expande secuencias de escape o no y reconoce opciones depende del contenido de las$PATH
y / o$_AST_FEATURES
variables de entorno.Si
$PATH
contiene un componente que contiene/5bin
o/xpg
antes del componente/bin
o/usr/bin
, entonces se comporta de la manera SysV / UNIX (expande secuencias, no acepta opciones). Si encuentra/ucb
o/bsd
primero o si contiene$_AST_FEATURES
7UNIVERSE = ucb
, entonces se comporta de la manera BSD 3 (-e
para permitir la expansión, reconoce-n
).El valor predeterminado depende del sistema, BSD en Debian (consulte la salida de
builtin getconf; getconf UNIVERSE
las versiones recientes de ksh93):3. BSD para echo -e?
La referencia a BSD para el manejo de la
-e
opción es un poco engañosa aquí. La mayoría de esosecho
comportamientos diferentes e incompatibles se introdujeron en AT&T:\n
,\0ooo
,\c
En el banco de trabajo del programador de UNIX (basado en Unix V6), y el resto (\b
,\r
...) en UNIX System III Ref .-n
en Unix V7 (por Dennis Ritchie Ref )-e
en Unix V8 (por Dennis Ritchie Ref )-E
posiblemente inicialmente provenía debash
(CWRU / CWRU.chlog en la versión 1.13.5 menciona que Brian Fox lo agregó en 1992-10-18, GNU loecho
copió poco después en sh-utils-1.8 lanzado 10 días después)Mientras que la
echo
orden interna de lossh
o BSD han apoyado-e
desde el día en que comenzaron a utilizar la cáscara Almquist para ello en los años 90, la versión autónoma deecho
utilidad para el día de hoy no lo soporta allí ( FreeBSDecho
todavía no admite-e
, aunque lo hace de soporte-n
como Unix V7 (y también\c
pero solo al final del último argumento)).El manejo de
-e
se agregó aksh93
'secho
cuando está en el universo BSD en la versión ksh93r lanzada en 2006 y puede deshabilitarse en el momento de la compilación.4. GNU echo cambio de comportamiento en 8.31
Desde coreutils 8.31 (y este commit ), GNU
echo
ahora expande las secuencias de escape por defecto cuando POSIXLY_CORRECT está en el entorno, para que coincida con el comportamiento debash -o posix -O xpg_echo
'secho
incorporado (ver informe de error ).5. macOS
echo
La mayoría de las versiones de macOS han recibido la certificación UNIX de OpenGroup .
Su
sh
construcciónecho
es compatible, ya que estábash
(una versión muy antigua) construida conxpg_echo
habilitada por defecto, pero suecho
utilidad independiente no lo es.env echo -n
no produce nada en lugar de-n<newline>
,env echo '\n'
produce en\n<newline>
lugar de<newline><newline>
.Ese
/bin/echo
es el de FreeBSD que suprime la salida de nueva línea si el primer argumento es-n
o (desde 1995) si el último argumento termina en\c
, pero no admite ninguna otra secuencia de barra invertida requerida por UNIX, ni siquiera\\
.6.
echo
implementaciones que pueden generar datos arbitrarios textualmenteHablando estrictamente, también podría contar que FreeBSD / macOS
/bin/echo
arriba (no el de su shellecho
) dondezsh
'secho -E - "$var"
oyash
' sECHO_STYLE=raw echo "$var"
(printf '%s\n' "$var"
) podrían escribirse:Las implementaciones que admiten
-E
y-n
(o pueden configurarse para) también pueden hacer:Y
zsh
'echo -nE - "$var"
(printf %s "$var"
podría escribirse)7.
_AST_FEATURES
y la ASTUNIVERSE
No
_AST_FEATURES
está destinado a ser manipulado directamente, se usa para propagar las configuraciones de configuración AST a través de la ejecución de comandos. La configuración debe hacerse a través de laastgetconf()
API (no documentada) . Enksh93
el interior , elgetconf
incorporado (habilitado conbuiltin getconf
o invocandocommand /opt/ast/bin/getconf
) es la interfaz paraastgetconf()
Por ejemplo, harías
builtin getconf; getconf UNIVERSE = att
para cambiar laUNIVERSE
configuración aatt
(haciendoecho
que se comporte como SysV entre otras cosas). Después de hacer eso, notará que la$_AST_FEATURES
variable de entorno contieneUNIVERSE = att
.fuente
echo
expandir las\x
secuencias en lugar del shell como parte de la sintaxis de las citas es que puede generar un byte NUL (otro diseño incorrectamente discutible de Unix son aquellos delimitados por nulo) cadenas, donde la mitad de las llamadas del sistema (comoexecve()
) no pueden tomar secuencias arbitrarias de bytes)printf
parece ser un problema ya que la mayoría de las implementaciones lo hacen mal. La única implementación correcta que conozco está enbosh
y la página de Sven Maschek no enumera los problemas con bytes nulos a través de\0
.Es posible que desee utilizar
printf
para sus opciones de formato.echo
es útil cuando se trata de imprimir el valor de una variable o una línea (simple), pero eso es todo.printf
básicamente puede hacer lo que la versión C puede hacer.Ejemplo de uso y capacidades:
Echo
:printf
:Fuentes:
fuente
echo
para imprimir una variable puede fallar si el valor de la variable contiene metacaracteres.Una "ventaja", si quiere llamarlo así, sería que no tiene que decirle cómo
echo
interpretar ciertas secuencias de escape como\n
. Sabe interpretarlos y no requerirá-e
que lo haga.(Nota: lo último
\n
es necesario, loecho
implica, a menos que des la-n
opción)versus
Tenga
\n
en cuenta el último enprintf
. Al final del día, es cuestión de gustos y requisitos lo que usas:echo
oprintf
.fuente
/usr/bin/echo
y elbash
incorporado. Eldash
,ksh
y lazsh
orden internaecho
no necesita-e
cambiar a ampliar caracteres escapados.printf '%s\n' 'foo\bar
.Una desventaja
printf
es el rendimiento porque el shell incorporadoecho
es mucho más rápido. Esto entra en juego particularmente en Cygwin, donde cada instancia de un nuevo comando causa una gran sobrecarga de Windows. Cuando cambié mi programa de eco pesado de usar/bin/echo
al eco del shell, el rendimiento casi se duplicó. Es una compensación entre portabilidad y rendimiento. No es una volcada para usar siempreprintf
.fuente
printf
está construido en la mayoría de los shells hoy en día (bash, dash, ksh, zsh, yash, algunos derivados de pdksh ... por lo que también incluye los shells que generalmente se encuentran en cygwin). Las únicas excepciones notables son algunospdksh
derivados.printf
para generar bytes nulos, pero algunas implementaciones interpretan `\ c 'en la cadena de formato aunque no deberían hacerlo.printf
POSIX no especifica el comportamiento de si lo usa\c
en la cadena de formato, por lo que lasprintf
implementaciones pueden hacer lo que quieran a ese respecto. Por ejemplo, algunos lo tratan igual que en el PWBecho
(hace que printf salga), ksh lo usa para\cA
caracteres de control (para el argumento de formato printf y para$'...'
pero no paraecho
niprint
!). ¿No está seguro de qué tiene que ver eso con la impresión de bytes NUL, o tal vez se refiere aksh
'sprintf '\c@'
?