¿Por qué las comparaciones de scripts de shell a menudo usan x VAR VAR = xyes?


Veo esto a menudo en los scripts de compilación de proyectos que usan autotools (autoconf, automake). Cuando alguien quiere comprobar el valor de una variable de shell, frecuentemente usa este modismo:

if test "x$SHELL_VAR" = "xyes"; then
...

¿Cuál es la ventaja de esto sobre simplemente comprobar el valor de esta manera:

if test $SHELL_VAR = "yes"; then
...

Me imagino que debe haber alguna razón por la que veo esto tan a menudo, pero no puedo averiguar qué es.

Author: jonner, 2008-10-06

7 answers

Si está utilizando un shell que hace sustitución simple y la variable SHELL_VAR no existe (o está en blanco), entonces debe tener cuidado con los casos de borde. Las siguientes traducciones sucederán:

if test $SHELL_VAR = yes; then        -->  if test = yes; then
if test x$SHELL_VAR = xyes; then      -->  if test x = xyes; then

El primero de ellos generará un error ya que el primer argumento de test ha desaparecido. El segundo no tiene ese problema.

Su caso se traduce como sigue:

if test "x$SHELL_VAR" = "xyes"; then  -->  if test "x" = "xyes"; then

Puede parecer un poco redundante ya que tiene tanto la comillas y la " x " pero también manejará una variable con espacios en ella, sin dar eso como dos argumentos al comando test.

La otra razón (aparte de las variables vacías) tiene que ver con el procesamiento de opciones. Si escribes:

if test "$1" = "abc" ; then ...

Y $1 tiene el valor -n o -z o cualquier otra opción válida para el comando test, la sintaxis es ambigua. El x en la parte delantera evita que se tome un guion principal como una opción para test.

Mantener cuenta que esto depende de la shell. Algunos shells (csh por ejemplo, creo) se quejarán amargamente si la variable de entorno no existe en lugar de simplemente devolver una cadena vacía).

 73
Author: paxdiablo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-02-12 03:39:44

La otra razón que nadie más ha mencionado es en relación con el procesamiento de opciones. Si escribes:

if [ "$1" = "abc" ]; then ...

Y $1 tiene el valor '-n', la sintaxis del comando test es ambigua; no está claro lo que estaba probando. La ' x ' en la parte delantera evita que un guion principal cause problemas.

Tienes que estar buscando en shells realmente antiguos para encontrar uno donde el comando test no tenga soporte para -n o -z; la Versión 7 (1978) test los incluyó. Se no es del todo irrelevante - algunas cosas de UNIX de la Versión 6 escaparon a BSD, pero en estos días, sería extremadamente difícil encontrar algo tan antiguo en uso actual.

No usar comillas dobles alrededor de valores es peligroso, como un número de otras personas señalaron. De hecho, si existe la posibilidad de que los nombres de los archivos contengan espacios (macOS X y Windows fomentan eso hasta cierto punto, y Unix siempre lo ha soportado, aunque herramientas como xargs lo hacen más difícil), entonces debe incluir el archivo nombres entre comillas dobles cada vez que los usas también. A menos que esté a cargo del valor (por ejemplo, durante el manejo de opciones, y establezca la variable en 'no' al inicio y 'sí' cuando se incluye un indicador en la línea de comandos), entonces no es seguro usar formas de variables sin comillas hasta que las haya probado seguras -- y también puede hacerlo todo el tiempo para muchos propósitos. O documente que sus scripts fallarán horriblemente si los usuarios intentan procesar archivos con espacios en blanco en los nombres. (Y hay otros los personajes de los que preocuparse también could los backsticks también podrían ser bastante desagradables, por ejemplo.)

 18
Author: Jonathan Leffler,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-10-07 22:00:01

Hay dos razones que conozco para esta convención:

Http://tldp.org/LDP/abs/html/comparison-ops.html

En una prueba compuesta, incluso citar la variable string podría no ser suficiente. [- n "string string " - o"a a" = "b b"] puede causar un error con algunas versiones de Bash si string la cadena está vacía. La forma segura es añadir un personaje extra a posiblemente variables vacías, ["x string string"!= x-o "x a a" = "x b b"] (las" x " se cancelan).

Segundo, en otros shells además de Bash, especialmente los más antiguos, las condiciones de prueba como '- z ' para probar una variable vacía no existían, así que mientras esto:

if [ -z "$SOME_VAR" ]; then
  echo "this variable is not defined"
fi

Funcionará bien en BASH, si está apuntando a la portabilidad en varios entornos UNIX donde no puede estar seguro de que el shell predeterminado será Bash y si soporta la condición de prueba-z, es más seguro usar el formulario if [ "x SOME SOME_VAR" = "x"] ya que siempre tendrá el efecto deseado. Esencialmente se trata de una antigua shell scripting truco para encontrar una variable vacía, y todavía se utiliza hoy en día para la compatibilidad hacia atrás a pesar de que hay métodos más limpios disponibles.

 10
Author: Jay,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-10-06 13:41:06

Recomiendo en su lugar:

if test "yes" = "$SHELL_VAR"; then

Ya que elimina lo feo x, y todavía resuelve el problema mencionado por https://stackoverflow.com/a/174288/895245 que $SHELL_VAR puede comenzar con - y leerse como una opción.

 3
Author: Ciro Santilli 新疆改造中心 六四事件 法轮功,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:02:26

Creo que es debido a

SHELLVAR=$(true)
if test $SHELLVAR  = "yes" ; then echo "yep" ; fi 

# bash: test: =: unary operator expected

Así Como

if test $UNDEFINEDED = "yes" ; then echo "yep" ; fi
# bash: test: =: unary operator expected

Y

SHELLVAR=" hello" 
if test $SHELLVAR = "hello" ; then echo "yep" ; fi
# yep 

Sin embargo, esto debería funcionar normalmente

SHELLVAR=" hello"
if test "$SHELLVAR" = "hello" ; then echo "yep" ; fi 
#<no output>

Pero cuando se queja en la salida en otro lugar, es difícil decir de qué se queja, supongo, así que

SHELLVAR=" hello"
if test "x$SHELLVAR" = "xhello" ; then echo "yep" ; fi 

Funciona igual de bien, pero sería más fácil de depurar.

 2
Author: Kent Fredric,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-10-06 13:00:42

Solía hacer eso en DOS cuando el SHELL_VAR podría ser indefinido.

 1
Author: Ken,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-10-06 12:51:46

Si no haces lo de "x SHELL SHELL_VAR", entonces si$SHELL_VAR no está definido, obtienes un error acerca de que "=" no es un operador monádico o algo así.

 1
Author: Paul Tomblin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-10-06 13:01:05