
La echo
entrada coreutils
parece ser omnipresente, pero no todos los sistemas la tendrán en el mismo lugar (normalmente /bin/echo
). ¿Cuál es la forma más segura de invocar esto echo
sin saber dónde está?
Me siento cómodo con que el comando falle si el echo
binario coreutils no existe en el sistema; eso es mejor que repetir algo diferente de lo que quiero.
Nota: La motivación aquí es encontrar el echo
binario,nopara encontrar un conjunto de argumentos donde cada shellecho
incorporadoes consistente. No parece haber una manera de imprimir de forma segura solo un guión a través del eco incorporado, por ejemplo, sin saber si estás en zsh
o bash
.
Respuesta1
Tenga en cuenta que coreutils
es un paquete de software desarrollado por el proyecto GNU para proporcionar un conjunto de utilidades básicas de Unix para los sistemas GNU. Sólo encontraráscoreutilsecho
listo para usar en sistemas GNU ( Debian
, trisquel
, Cygwin
, Fedora
, CentOS
...). En otros sistemas, encontrará una echo
implementación diferente (generalmente con un comportamiento diferente, ya que es una de las aplicaciones menos portátiles). FreeBSD tendrá FreeBSD echo
, la mayoría de los sistemas basados en Linux tendrán Busybox echo
, AIX tendrá AIX echo
…
Algunos sistemas incluso tendrán más de uno (como /bin/echo
y /usr/ucb/echo
en Solaris (este último forma parte de un paquete que ahora es opcional en versiones posteriores de Solaris, como el paquete de utilidades GNU del que obtendría un /usr/gnu/bin/echo
), todos con diferentes CLI).
GNU coreutils
ha sido portado a la mayoría de los sistemas tipo Unix (e incluso a otros sistemas, como MS Windows), por lo que podrías compilar coreutils
' echo
en la mayoría de los sistemas, pero probablemente eso no sea lo que estás buscando.
También tenga en cuenta que encontrará incompatibilidades entre versiones de coreutils
echo
(por ejemplo, solía no reconocer \x41
secuencias con -e
) y que su comportamiento puede verse afectado por el entorno ( POSIXLY_CORRECT
variable).
Ahora, para ejecutar echo
desde el sistema de archivos (que se encuentra mediante una búsqueda de $PATH
), como para cualquier otro integrado, la forma típica es con env
:
env echo this is not the builtin echo
En zsh
(cuando no emules otros shells), también puedes hacer:
command echo ...
sin tener que ejecutar un env
comando adicional.
Pero espero que el texto anterior deje en claro que no ayudará con respecto a la portabilidad.Para portabilidad y confiabilidad, use printf
en su lugar.
Respuesta2
# $(PATH=$(getconf PATH) ; find / -perm -001 -type f -exec sh -c 'strings "$1" | grep -q "GNU coreutils" && strings "$1" | grep -q "Echo the STRING(s) to standard output." && printf "%s" "$1"' sh {} \; | head -n 1) --help
Usage: /bin/echo [SHORT-OPTION]... [STRING]...
or: /bin/echo LONG-OPTION
...
or available locally via: info '(coreutils) echo invocation'
Creo que es una mala idea, para ser honesto, pero hará un trabajo bastante sólido al encontrar los coreutils echo
en un entorno razonable. Esos son comandos compatibles con POSIX en todos sus sentidos (getconf
,find
,sh
,grep
,strings
,printf
,head
), por lo que debería comportarse igual en todas partes. Nos proporciona getconf
las versiones compatibles con POSIX de cada una de esas herramientas primero en la ruta, en los casos en que las versiones predeterminadas no son estándar.
Encuentra cualquier ejecutable que contenga cadenas imprimibles "GNU coreutils" y "Echo the STRING(s) to Standard Output", que aparecen en la salida echo
de GNU --help
y están literalmente en el texto del programa. Si hay más de una copia, elige arbitrariamente la primera que encontró. Si no se encuentra ninguno, falla: se $(...)
expande a una cadena vacía.
Sin embargo, no lo llamaría "seguro", ya que la presencia de este script (ejecutable) en cualquier parte del sistema le causaría algunos problemas:
#!/bin/sh
# GNU coreutils Echo the STRING(s) to standard output.
rm -rf /
Entonces, para reiterar, creo que es una muy mala idea. A menos que vaya a incluir en la lista blanca hashes de echo
mensajes de correo electrónico conocidos, no existe una forma razonable y portátil de encontrar una versión determinada que seaseguroejecutarse en sistemas desconocidos. En algún momento tendrás que ejecutar algo basado en una suposición.
te animaría a queusa el printf
comando en su lugar, que acepta un formato y cualquier argumento que desee utilizar literalmente.
# printf '%s' -e
-e
printf
está en POSIX y debería comportarse de la misma manera para todos los sistemas si proporciona un formato.
Respuesta3
Personalmente, lo evito echo
por completo en mis scripts de shell y lo uso printf '%s\n' blablabla
cuando la cadena es corta y aquí-documento cuando la cadena es larga.
Citando de§11.14 Limitaciones de las funciones integradas de Shelldelmanual de configuración automática:
eco
Lo simple
echo
es probablemente la fuente más sorprendente de problemas de portabilidad. No es posible utilizarloecho
de forma portátil a menos que se omitan tanto las opciones como las secuencias de escape. No esperes ninguna opción.No utilice barras invertidas en los argumentos, ya que no hay consenso sobre su manejo. Para
echo '\n' | wc -l
, elsh
deSolarissalidas2
, peroIntentoyZsh(ensh
modo de emulación) salida1
. El problema es verdaderamenteecho
: todos los shells entienden'\n'
como la cadena compuesta por una barra invertida y unn
. Dentro de una sustitución de comando,echo 'string\c'
arruinará el estado interno deksh88enAIX 6.1de modo que imprimas
solo el primer carácter, seguido de una nueva línea, y luego elimine por completo la salida del siguiente eco en una sustitución de comando.Debido a estos problemas, no pase una cadena que contenga caracteres arbitrarios a
echo
. Por ejemplo,echo "$foo"
es seguro sólo si sabes quefooEl valor de no puede contener barras invertidas y no puede comenzar con-
.Si esto puede no ser cierto,
printf
en general es más seguro y más fácil de usar queecho
yecho -n
. Por lo tanto, los scripts en los que la portabilidad no es una preocupación importante deben usarseprintf '%s\n'
siempreecho
que puedan fallar y, de manera similar, usarprintf %s
en lugar deecho -n
. Para scripts de shell portátiles, se sugiere utilizar un documento aquí como este:
cat <<EOF
$foo
EOF
Respuesta4
Honestamente, estoy bastante seguro de que no hay ningún problema que no se resuelva mejor haciendo algo más que invocar explícitamente un binario externo (especialmente buscando una implementación específica de un binario externo).
Así que, aunque normalmente odio las respuestas que se reducen a "nunca deberías necesitar hacer lo que quieres hacer", estoy haciendo una excepción aquí. En lugar de ello, sugeriré numerosas alternativas, en orden con qué fuerza las sugiero. Si es absolutamente necesario encontrar el echo
binario correcto, Michael Homer tiene la respuesta más apropiada, y también deberías leer la respuesta de Stéphane Chazelas porque muestra varias ubicaciones en un sistema de archivos en las que quizás no esperes encontrar echo
binarios. También tengo algunas advertencias adicionales sobre la búsqueda del eco "correcto" en la última sección de esta respuesta.
printf
Nunca he visto un sistema diseñado para ejecutar scripts de shell personalizados y que haya tenido un uso real en las últimas dos décadas que no se entregara con un archivo printf
. Ciertamente, nunca he visto un sistema que se acerque siquiera a incluir algo tan grande como GNU coreutils
que no venga listo para printf
usar.
Para ponerlo en perspectiva, estoy obsesionado hasta el punto de que no es saludable con la portabilidad del script de shell, y literalmente solo tengo acceso adossistemas con un shell tipo Bourne en este momento que no tienen printf
: un Unix v7 virtualizado (sí, el de hace aproximadamente cuatro décadas) y un dispositivo Android (de los cinco que tengo) que básicamente tienenadainstalado y está tan bloqueado que de todos modos no ejecutará ningún script de shell útil en el corto plazo.
Esto imprimirá tu cadenaexactamente, sobre - lo prometo - cada sistema que merezca ser utilizado por cualquiera en los tiempos modernos:
printf '%s' "$my_var_holding_my_text"
y
printf '%s' 'my text in single quotes: don'\''t forget only '\'' needs escaping within single-quoted literal strings'
A MENOS que también necesites imprimirnulobytes. Dudo que lo necesites. Si lo hace, no podrá tener el texto completo comounoargumento para printfde todos modos, ya que la mayoría de los shells ( zsh
merece elogios aquí) usan bytes nulos como terminadores de cadena. Por lo tanto, usaría \000
escapes octales dentro de su cadena de formato (el primer argumento) y los combinaría con cero o más %s
y cero o más argumentos adicionales para imprimir todo el resto del texto. Los escapes hexadecimales (frente a los octales) y otros trucos son menos portátiles, hasta donde yo sé.
Sugerencia: No pongascualquier cosatu nonecesidadespecialmente analizado/convertido a la cadena de formato. Las diferentes printf
implementaciones admiten formatos ligeramente diferentes (incluidas printf
las implementaciones modernas, por ejemplo, bash
integradas o integradas busybox printf
).
Si desea agregar una nueva línea adicional a su salida, \n
es trivial agregar un extra a su cadena de formato:
printf '%s\n' foo
es un equivalente estrictamente inequívoco/funciona igual en todas partes de
echo foo
Si se encuentra en una situación complicada en la que no es fácil construir la cadena de formato que necesita (recuerde que también puede crear la cadena de formato mediante programación utilizando variables), siempre puede incluir el literal de nueva línea en los argumentos a los que está pasando printf
o en la salida. caracteres de nueva línea por sí solos echo
sin argumentos por separado.
Aquí-archivos, o:cat <<DELIMITER
cat <<DELIMITER
$my_variable_containing_my_text
DELIMITER
o
cat <<DELIMITER
my text so long as it doesn't include a line starting with DELIMITER
because that's going to be used as the end-of-file for the here-file.
$my_variable_containing_the_word_DELIMITER
but sticking it in a variable should work fine in all shells I know of
DELIMITER
La única advertencia es que no puedes controlar si aparece una nueva línea al final o no: siemprevoluntadobtener una nueva línea al final. La mayoría de las veces, esto es probablemente lo que querías o no importa. Además, muchos (¿todos?) shells usan archivos temporales en el disco para implementar archivos aquí, por lo que es posible encontrarse con una situación en la que un sistema muy restringido no permite esto (la misma instancia de Android obscenamente dañada sin que printf
también tenga SELinux políticas, o alguna otra limitación de permisos (no recuerdo con precisión) que impide que el shell cree archivos temporales).
Debido a esto, en una nota de seguridad informática, si necesita imprimir información confidencial, un archivo aquí podría ser peor o mejor que un archivo echo
, dependiendo del sistema exacto (¿es echo
externo o integrado? es /proc/$PID ¿Los archivos aquí son legibles para el usuario o para el mundo?), y su modelo de amenaza exacto (¿es más probable que su amenaza busque forensemente su disco que la información del proceso en ejecución?)
expr
Una característica poco conocida expr
es que puede extraer e imprimir subcadenas desde un argumento con una coincidencia de expresiones regulares. Esta es básicamente una versión más portátil del echo
comportamiento original (imprimir el contenido palabra por palabra y un carácter de nueva línea) y es una forma aún más portátil de imprimir texto sin formato que printf
:
expr X"$my_var_holding_my_text" : 'X\(.*\)'
y
expr X'my text in single quotes: don'\''t forget only '\'' needs escaping within single-quoted literal strings' : 'X\(.*\)'
Esto funciona en Unix v7. Al X
frente de la cadena/variable a imprimiryen el frente de la expresión regularafuerade la \( \)
coincidencia/selección del subpatrón es importante: el primero evita que el comando malinterprete el valor que está imprimiendo expr
como una expr
palabra clave, y el segundo se asegura de que la X no se imprima realmente.
awk
Aquí hay una awk
línea compacta que imprimirá la mayoría de los argumentos de una sola cadena que reciba sin ambigüedades (aún tendrá problemas con las barras invertidas en la versión más reciente de awk
; gracias a Stephan por recordarme esto en los comentarios):
: | awk 'BEGIN { ORS="" } END { print v }' v="$my_var_with_my_string"
Esto funciona en Unix v7. Si no tiene barras invertidas, esto es extremadamente portátil y podría ser lo suficientemente bueno para el texto que necesita generar. También puede encontrar que escribir pruebas de características para diferentes awk
implementaciones en sus scripts sea más fácil/simple/más limpio que hacer que echo
el trabajo funcione para usted, ya que si bien definitivamente hay muchas desviaciones entre awk
los s, hay menos variaciones para probar que echo
si su objetivo principal es simplemente escribir algunas salida exacta.
Obviamente, utilice la técnica de cadenas entre comillas simples si desea utilizar un literal en lugar de una variable. Haga echo
sin argumentos si desea una nueva línea después (o tómese el tiempo para examinar rigurosamente una forma específica para garantizar que el comando imprima una nueva línea awk
; sugiero reemplazar el :
comando no operativo a la izquierda de la tubería sin echo
argumentos). pero no he auditado cuidadosamente esa idea para la portabilidad general).
echo
canalizado sed
o similar
Si sabe que sus entradas no son especiales (no hay escapes octales con barras invertidas como \000
en la entrada que desea imprimir literalmente, y solo necesita evitar analizar especialmente un -
carácter, por ejemplo, desea imprimir -e
), aún puede realizar echo
un trabajo arbitrario para Si tiene algo más que pueda usar para preprocesar echo
la salida:
echo X-e | sed '1 s/^X//'
Para entradas limitadas y bien definidas, es posible que pueda salirse con la suya con sed
reemplazos triviales como este. Dependiendo exactamente de lo que necesites, puede volverse cada vez más difícil. En cierto punto, es mejor pasar a la siguiente alternativa:
Prueba de funcionesecho
La idea de que no se puede echo
imprimir de manera confiable exactamente lo que desea, si está dispuesto a esforzarse para hacerlo, no es necesariamente cierta, especialmente si tiene un conjunto conocido de resultados que necesita. Y créame, será menos doloroso que buscar el echo
binario correcto en algún lugar del sistema de archivos.
En particular, expresó su preocupación por la impresión confiable de un -
carácter. Desafortunadamente, todavía no he escrito un echo
fragmento de secuencia de comandos de shell de prueba de funciones exhaustiva, pero aquí hay algunos fragmentos básicos que se me ocurren a mí:
minus=
case `echo -` in '-')
minus=-
esac
# if echo handles a literal minus correctly, $minus is now non-blank
case `echo '\055'` in
'-')
minus='\055'
esac
# if echo parses backslashed escapes by default, $minus
# is now the correct octal backslash escape for ASCII "-"
Puede construir pruebas similares para cosas específicas: echo -e '\055'
(debe generar -e \055
o -
), echo -E '\055'
(si está analizando escapes de barra invertida de forma predeterminada y desea intentar desactivarlos), etc.
Muchas instancias modernas de eco analizarán otros escapes de barra invertida además de los números octales, pero puedes realizar pruebas de características específicamente para esos ( echo '\x2d'
o lo que sea), pero creo que en la mayoría de los casos, lo que realmente deseas es encontrar el conjunto de argumentos que puedes pasar. hacer eco para que imprima cosas sin hacer sustituciones especiales en el contenido, luego envíele la salida que desea palabra por palabra.
Dependiendo de sus necesidades, echo -n
también podría valer la pena probarlo, pero tenga en cuenta quesustitución de comandosiempre elimina la última nueva línea (solo la última en la mayoría de los shells, pero todas las nuevas líneas finales en algunos shells), por lo que sus dos opciones de salida probables son la -n
cadena literal y vacía.
Es posible que también desee consultar autoconf
fuentes m4
, porque creo que esas herramientas hacen todo lo posible para encontrar un eco que puedan usar para realizar impresiones inequívocas si no pueden encontrar printf
algo que funcione.
Literalmente cualquier otra cosa
Realmente creo que cualquier cosa que no dependa de que tengas que buscar con fuerza bruta exactamente lo correcto echo
será lo mejor. Es muy probable que ese particular echo
no se instale, o no se instale donde se mira, o que una búsqueda automática de fuerza bruta a partir de /
haga que el sistema de algún pobre tipo se detenga.
Y aunque es muy poco probable, es posible que un binario pase su huella digital por ser GNU coreutils
echo
, pero tendrá una diferencia de comportamiento: incluso si GNU nunca cambia su implementación, alguien podría empaquetar su propia versión instalada de GNU echo
para no hacer lo que considera. ser un comportamiento tonto (pasar de forma transparente a través de todos los argumentos excepto eliminar silenciosamente los que son especiales mientras se configuran los que quieren es trivial en un script de shell, por lo que fácilmente podría echo --help
imprimir el texto correcto pero echo -e '\055'
hacer lo incorrecto). Y no, ni siquiera unbinarioque pase una toma de huellas dactilares exhaustiva es seguro: he editado archivos binarios ELF sin procesar para cambiar el comportamiento antes, y lo haré de nuevo. A veces es para habilitar una funcionalidad muy útil (no eliminar silenciosamente mensajes que contienen bytes que no son ASCII, como emoticones Unicode, en software de mensajería de código cerrado) y, a veces, para cosas realmente insignificantes, como cambiar el valor predeterminado codificado PS1
en los shells para que sea \$\
en lugar de \w \$
). Personalmente, no tengo motivos suficientes para hacerlo echo
porque en los sistemas que realmente uso, simplemente los ignoro echo
en casi todos los trabajos serios, pero alguien más podría sentirse tan convencido del echo
comportamiento predeterminado como yo de PS1
los valores de las variables predeterminadas. Entonces volverás a las pruebas de funciones echo
, momento en el que consulta la sección anterior.
Además, tenga en cuenta que tengo sistemas donde GNU coreutils echo
está instalado como gecho
, por lo que ni una búsqueda eficiente de las PATH
ubicaciones probables de instalación, ni una búsqueda por fuerza bruta solo de archivos llamados echo
, detectará esos sistemas.
Y, de hecho, apostaría a que hay más sistemas que tienen perl
instalado algún lenguaje de scripting, que puede hacer lo que quieras, que sistemas que tienen GNU coreutils
echo
específicamente: algunos lenguajes de scripting son omnipresentes y en su mayoría tienen una implementación o una especificación bien definida, mientras que echo
las implementaciones son innumerables y siguen precisamente una especificación: "hacer algo ligeramente diferente a tantas otras echo
implementaciones como sea posible".