Saltar al contenido

¿Por qué printf es mejor que echo?

Ya no busques más por todo internet porque llegaste al sitio perfecto, poseemos la respuesta que quieres recibir y sin complicarte.

Solución:

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 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, 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 citas de shell: $'...' que luego fue copiado por bash y zsh pero ya era demasiado tarde para ese momento.

Ahora, cuando un UNIX estándar 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 ve c en un argumento, deja de generar (por lo que la nueva línea final tampoco se genera).

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

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

Sobre estos echo implementaciones que admiten opciones, generalmente no hay soporte para un -- para marcar el final de las opciones (el echo incorporado de algunos shells no similares a Bourne, y zsh admite - para eso), por ejemplo, es difícil generar "-n" con echo en muchas conchas.

En algunas conchas como bash¹ o ksh93² o yash ($ECHO_STYLE variable), el comportamiento incluso depende de cómo se compiló el shell o del entorno (GNU echoEl comportamiento también cambiará si $POSIXLY_CORRECT está en el medio ambiente y con la versión4, zshestá con su bsd_echo opción, algunos basados ​​en pdksh con su posix opción o si se llaman como sh o no). Asi que dos bashechos, 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 está especificado. bash echo en ese sentido no es POSIX en eso, por ejemplo echo -e no está dando salida -e como POSIX requiere. 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 son compatibles. Incluso algunos certificado sistemas como macOS5 no cumplen.

Para representar realmente la realidad actual, POSIX debería decir: si el primer argumento coincide con el ^-([eEn]*|-help|-version)$ expresión regular extendida o cualquier argumento contiene barras invertidas (o caracteres cuya codificación contiene la codificación del carácter de barra invertida como α en configuraciones regionales que utilizan el juego de caracteres BIG5), el comportamiento no está especificado.

En general, no sabes qué echo "$var" saldrá 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 está tomando entradas externas (del usuario como argumentos o nombres de archivo del sistema de archivos …), no puede usar echo para mostrarlo.

Esto esta bien:

echo >&2 Invalid file.

Esto no es:

echo >&2 "Invalid file: $file"

(Aunque funcionará bien con algunos (no compatibles con UNIX) echo implementaciones como bashes 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 mediante el 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 yashlas variables no pueden contener secuencias arbitrarias de bytes, por lo tanto, no nombres de archivos arbitrarios) y zsh‘s echo -E - "$var"6).

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

printf '%sn' "$var"

Producirá 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 características que especifica POSIX, pero también hay muchas extensiones. Por ejemplo, algunos apoyan un %q para citar los argumentos, pero la forma en que se hace varía de un caparazón a otro, algo de soporte uxxxx para caracteres Unicode. El comportamiento varía para printf '%10sn' "$var" en configuraciones regionales de varios bytes, hay al menos tres resultados diferentes para printf %b '123'

Pero al final, si se ciñe al conjunto de funciones POSIX de printf y no intentes hacer nada demasiado sofisticado 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.

Un mas confiable echo se puede implementar usando printf, igual que:

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. como bash‘s echo el comportamiento se puede alterar.

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_echobash 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 medio ambiente o con el posix opción:

Comportamiento predeterminado en la mayoría de los sistemas:

$ bash -c 'echo -n "101"'
101% # the % here denotes the absence of newline character

xpg_echo expande secuencias según lo requiera UNIX:

$ BASHOPTS=xpg_echo bash -c 'echo "101"'
A

Todavía honra -n y -e (y -E):

$ BASHOPTS=xpg_echo bash -c 'echo -n "101"'
A%

Con xpg_echo y modo POSIX:

$ env BASHOPTS=xpg_echo POSIXLY_CORRECT=1 bash -c 'echo -n "101"'
-n A
$ env BASHOPTS=xpg_echo sh -c 'echo -n "101"' # (where sh is a symlink to bash)
-n A
$ env BASHOPTS=xpg_echo SHELLOPTS=posix bash -c 'echo -n "101"'
-n A

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 genera -e en:

$ env SHELLOPTS=posix bash -c 'echo -e'

$

Los valores predeterminados para xpg_echo y posix se pueden definir en tiempo de compilación con el --enable-xpg-echo-default y --enable-strict-posix-default opciones a la configure texto. Eso es lo que suelen hacer las versiones recientes de OS / X para construir su /bin/sh. Ninguna implementación / distribución Unix / Linux en su sano juicio normalmente haría eso por /bin/bash aunque. De hecho, eso no es true, los /bin/bash que Oracle se envía con Solaris 11 (en un paquete opcional) parece estar construido con --enable-xpg-echo-default (ese no fue el caso en Solaris 10).

2. ¿Cómo ksh93‘s echo el comportamiento se puede alterar.

En ksh93, ya sea echo expande las secuencias de escape o no y reconoce las opciones depende del contenido de la $PATH y / o $_AST_FEATURES Variables de entorno.

Si $PATH contiene un componente que contiene /5bin o /xpg antes de /bin o /usr/bin componente entonces se comporta de la manera SysV / UNIX (expande secuencias, no acepta opciones). Si encuentra /ucb o /bsd primero o si $_AST_FEATURES7 contiene UNIVERSE = ucb, entonces se comporta el BSD3 camino (-e para permitir la expansión, reconoce -n).

El valor predeterminado depende del sistema, BSD en Debian (consulte el resultado de builtin getconf; getconf UNIVERSE en versiones recientes de ksh93):

$ ksh93 -c 'echo -n' # default -> BSD (on Debian)
$ PATH=/foo/xpgbar:$PATH ksh93 -c 'echo -n' # /xpg before /bin or /usr/bin -> XPG
-n
$ PATH=/5binary:$PATH ksh93 -c 'echo -n' # /5bin before /bin or /usr/bin -> XPG
-n
$ PATH=/5binary:$PATH _AST_FEATURES='UNIVERSE = ucb' ksh93 -c 'echo -n' # -> BSD
$ PATH=/ucb:/foo/xpgbar:$PATH ksh93 -c 'echo -n' # /ucb first -> BSD
$ PATH=/bin:/foo/xpgbar:$PATH ksh93 -c 'echo -n' # /bin before /xpg -> default -> BSD

3. BSD para eco -e?

La referencia a BSD para el manejo de la -e La opción aquí es un poco engañosa. La mayoría de esos diferentes e incompatibles echo Todos los comportamientos se introdujeron en AT&T:

  • n, ooo, c en Programmer’s Work Bench UNIX (basado en Unix V6), y el resto (b, r…) en Unix System IIIÁrbitro.
  • -n en Unix V7 (por Dennis RitchieÁrbitro)
  • -e en Unix V8 (por Dennis RitchieÁrbitro)
  • -E en sí mismo posiblemente inicialmente provino de bash (CWRU / CWRU.chlog en la versión 1.13.5 menciona que Brian Fox lo agregó en 1992-10-18, GNU echo copiándolo poco después en sh-utils-1.8 lanzado 10 días después)

Mientras que la echo incorporado del sh de BSD han admitido -e desde el día en que empezaron a usar el caparazón de Almquist a principios de los 90, el echo la utilidad hasta el día de hoy no lo admite allí (FreeBSD echo todavía no es compatible -e, aunque es compatible -n como Unix V7 (y también c pero solo al final del último argumento)).

El manejo de -e fue agregado a ksh93‘s echo cuando en el BSD universo en la versión ksh93r lanzada en 2006 y se puede desactivar en el momento de la compilación.

4. Cambio de comportamiento de eco de GNU en 8.31

Desde coreutils 8.31 (y esta confirmación), GNU echo ahora expande las secuencias de escape de forma predeterminada cuando POSIXLY_CORRECT está en el entorno, para que coincida con el comportamiento de bash -o posix -O xpg_echo‘s echo 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 incorporado echo es compatible ya que es bash (una versión muy antigua) construida con xpg_echo habilitado de forma predeterminada, pero su independiente echo la utilidad no lo es. env echo -n no produce nada en lugar de -n, env echo 'n' salidas n en lugar de .

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 palabra por palabra

Estrictamente hablando, también podría contar que FreeBSD / macOS /bin/echo arriba (no su caparazón echo incorporado) donde zsh‘s echo -E - "$var" o yash‘s ECHO_STYLE=raw echo "$var" (printf '%sn' "$var") podría escribirse:

/bin/echo "$var
c"

Y zsh‘s echo -nE - "$var" (printf %s "$var") podría ser escrito

/bin/echo "$varc"

Implementaciones que apoyan -E y -n (o se puede configurar para) también puede hacer:

echo -nE "$var
"

Por el equivalente de printf '%sn' "$var".

7. _AST_FEATURES y el AST UNIVERSE

los _AST_FEATURES no está diseñado para manipularse directamente, se utiliza para propagar los valores de configuración de AST a través de la ejecución de comandos. La configuración está destinada a realizarse a través del (indocumentado) astgetconf() API. Dentro ksh93, los getconf incorporado (habilitado con builtin getconf o invocando command /opt/ast/bin/getconf) es la interfaz para astgetconf()

Por ejemplo, harías builtin getconf; getconf UNIVERSE = att para cambiar el UNIVERSE ajuste a att (causando echo comportarse al estilo SysV entre otras cosas). Después de hacer eso, notarás el $_AST_FEATURES la variable de entorno contiene UNIVERSE = att.

Es posible que desee utilizar printf por 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:

echo "*** Backup shell script ***"
echo
echo "Runtime: $(date) @ $(hostname)"
echo

printf:

vech="bike"
printf "%sn" "$vech"

Fuentes:

  • http://bash.cyberciti.biz/guide/Echo_Command
  • hombre echo
  • hombre printf

Una “ventaja”, si quieres llamarlo así, sería que no tienes que decirlo como echo interpretar ciertas secuencias de escape como n. Sabe interpretarlos y no requerirá un -e para hacerlo.

printf "somenmulti-linedntextn"

(NB: el último n es necesario, echo lo implica, a menos que le des la -n opción)

versus

echo -e "somenmulti-linedntext"

Tenga en cuenta el último n en printf. Al final del día es cuestión de gustos y requisitos lo que usas: echo o printf.

valoraciones y comentarios

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *