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 agregaron echo
.
David Korn se dio cuenta del error e introdujo una nueva forma de citas de shell: $'...'
que luego fue copiada bash
y zsh
que ya era demasiado tarde.
Ahora, cuando 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 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 una echo
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 (la echo
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"
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 ( echo
el comportamiento de GNU también cambiará si $POSIXLY_CORRECT
está en el entorno y con la versión 4 , zsh
con su bsd_echo
opción, algunos basados en pdksh con su posix
opción o si se les llama como sh
o no). Por lo tanto, dos bash
echo
s, incluso de la misma versión de bash
, 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 especificar
En 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 usemos printf
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 usar echo
para mostrarlo.
Esto esta bien:
echo >&2 Invalid file.
Esto no es:
echo >&2 "Invalid file: $file"
(Aunque funcionará bien con algunas echo
implementaciones (no compatibles con UNIX) como bash
's cuando la xpg_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 son yash
con ECHO_STYLE=raw
(con la advertencia de que yash
las variables no pueden contener secuencias arbitrarias de bytes, por lo que no son 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 '%s\n' "$var"
Producirá el contenido $var
seguido de un carácter de nueva línea, independientemente de qué carácter pueda contener.
printf '%s' "$var"
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ía printf '%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 usando printf
, como:
echo() ( # subshell for local scope for $IFS
IFS=" " # needed for "$*"
printf '%s\n' "$*"
)
echo_n() (
IFS=" "
printf %s "$*"
)
echo_e() (
IFS=" "
printf '%b\n' "$*"
)
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í:
echo() {
if [ "$#" -gt 0 ]; then
printf %s "$1"
shift
fi
if [ "$#" -gt 0 ]; then
printf ' %s' "$@"
fi
printf '\n'
}
Notas
1. ¿Cómo bash
's echo
comportamiento puede ser alterado.
Con bash
, en tiempo de ejecución, hay dos cosas que controlan el comportamiento de echo
(al lado enable -n echo
o redefinir echo
como una función o alias): la 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 la posix
opción:
Comportamiento predeterminado en la mayoría de los sistemas:
$ bash -c 'echo -n "\0101"'
\0101% # the % here denotes the absence of newline character
xpg_echo
expande las secuencias como UNIX requiere:
$ BASHOPTS=xpg_echo bash -c 'echo "\0101"'
A
Todavía honra -n
y -e
(y -E
):
$ BASHOPTS=xpg_echo bash -c 'echo -n "\0101"'
A%
Con xpg_echo
y modo POSIX:
$ env BASHOPTS=xpg_echo POSIXLY_CORRECT=1 bash -c 'echo -n "\0101"'
-n A
$ env BASHOPTS=xpg_echo sh -c 'echo -n "\0101"' # (where sh is a symlink to bash)
-n A
$ env BASHOPTS=xpg_echo SHELLOPTS=posix bash -c 'echo -n "\0101"'
-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 se genera -e
en:
$ env SHELLOPTS=posix bash -c 'echo -e'
$
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 el configure
script. Eso suele ser lo que hacen las versiones recientes de OS / X para construir su /bin/sh
. Sin /bin/bash
embargo, ninguna implementación / distribución de Unix / Linux en su sano juicio lo haría normalmente . En realidad, eso no es cierto, /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
's echo
comportamiento puede ser alterado.
En ksh93
, si echo
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):
$ 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 echo -e?
La referencia a BSD para el manejo de la -e
opción es un poco engañosa aquí. La mayoría de esos echo
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 de bash
(CWRU / CWRU.chlog en la versión 1.13.5 menciona que Brian Fox lo agregó en 1992-10-18, GNU lo echo
copió poco después en sh-utils-1.8 lanzado 10 días después)
Mientras que la echo
orden interna de los sh
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 de echo
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ó a ksh93
's echo
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 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
construcción echo
es compatible, ya que está bash
(una versión muy antigua) construida con xpg_echo
habilitada por defecto, pero su echo
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 textualmente
Hablando estrictamente, también podría contar que FreeBSD / macOS /bin/echo
arriba (no el de su shell echo
) donde zsh
's echo -E - "$var"
o yash
' s ECHO_STYLE=raw echo "$var"
( printf '%s\n' "$var"
) podrían escribirse:
/bin/echo "$var
\c"
Las implementaciones que admiten -E
y -n
(o pueden configurarse para) también pueden hacer:
echo -nE "$var
"
Y zsh
' echo -nE - "$var"
( printf %s "$var"
podría escribirse)
/bin/echo "$var\c"
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 la astgetconf()
API (no documentada) . En ksh93
el interior , el getconf
incorporado (habilitado con builtin getconf
o invocando command /opt/ast/bin/getconf
) es la interfaz paraastgetconf()
Por ejemplo, harías builtin getconf; getconf UNIVERSE = att
para cambiar la UNIVERSE
configuración a att
(haciendo echo
que se comporte como SysV entre otras cosas). Después de hacer eso, notará que la $_AST_FEATURES
variable de entorno contiene UNIVERSE = att
.
echo -e
?