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 echo
El comportamiento también cambiará si $POSIXLY_CORRECT
está en el medio ambiente y con la versión4, zsh
está 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 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 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 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 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 yash
las 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_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 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 . De hecho, eso no es true, los /bin/bash
aunque/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_FEATURES
7 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
,