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

votos
43

Veo esto a menudo en las secuencias de comandos de compilación de proyectos que usan autotools (autoconf, automake). Cuando alguien quiere verificar el valor de una variable de shell, con frecuencia utilizan este modismo:

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

¿Cuál es la ventaja de esto simplemente al verificar 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 descifrar qué es.

Publicado el 06/10/2008 a las 11:50
fuente por usuario
En otros idiomas...                            


7 respuestas

votos
1

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

Respondida el 06/10/2008 a las 11:51
fuente por usuario

votos
2

Creo que es debido a

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

# bash: test: =: unary operator expected

tanto 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 generalmente debería funcionar

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

pero cuando se queja en otro lugar, es difícil decir de qué se queja, 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.

Respondida el 06/10/2008 a las 12:00
fuente por usuario

votos
1

Si no haces la cosa "x $ SHELL_VAR", entonces si $ SHELL_VAR no está definido, obtienes un error sobre "=" no ser un operador monádico o algo así.

Respondida el 06/10/2008 a las 12:01
fuente por usuario

votos
69

Si está usando un shell que hace una sustitución simple y la SHELL_VARvariable no existe (o está en blanco), entonces debe tener cuidado con los casos límite. 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 estos generará un error ya que el primer argumento testha desaparecido. El segundo no tiene ese problema.

Su caso se traduce de la siguiente manera:

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

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

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

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

y $1tiene el valor -nyo -zcualquier otra opción válida para el testcomando, la sintaxis es ambigua. El xfrente evita que se recoja un guion delantero como opción test.

Tenga en cuenta que esto depende del caparazón. Algunos shells ( cshpor ejemplo, creo) se quejarán amargamente si la variable de entorno no existe en lugar de simplemente devolver una cadena vacía).

Respondida el 06/10/2008 a las 12:29
fuente por usuario

votos
10

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 de cadena podría no ser suficiente. [-n "$ string" -o "$ a" = "$ b"] puede causar un error con algunas versiones de Bash si $ string está vacío. La forma más segura es agregar un carácter adicional a variables posiblemente vacías, ["x $ string"! = X -o "x $ a" = "x $ b"] (la cancelación de "x").

En segundo lugar, en otros shells que Bash, especialmente en los más antiguos, las condiciones de prueba como '-z' para probar una variable vacía no existían, por lo tanto, mientras esto:

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

funcionará bien en BASH, si está buscando la portabilidad en varios entornos UNIX donde no puede estar seguro de que el shell predeterminado sea Bash y si admite la condición de prueba -z, es más seguro usar el formulario if [" x $ SOME_VAR "=" x "] ya que eso siempre tendrá el efecto deseado. Básicamente, este es un viejo truco de shell scripting para encontrar una variable vacía, y todavía se usa hoy en día para compatibilidad con versiones anteriores a pesar de que hay métodos más limpios disponibles.

Respondida el 06/10/2008 a las 12:41
fuente por usuario

votos
18

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

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

y $ 1 tiene el valor '-n', la sintaxis del comando de prueba es ambigua; no está claro lo que estabas probando. La 'x' en la parte delantera evita que un guión de ataque cause problemas.

Tienes que estar mirando conchas realmente antiguas para encontrar una donde el comando de prueba no tenga soporte para -no -z; el testcomando Versión 7 (1978) los incluyó. No es del todo irrelevante: algunas versiones 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 el uso actual.

No usar comillas dobles para los valores es peligroso, como señalaron otras personas. De hecho, si existe la posibilidad de que los nombres de los archivos contengan espacios (MacOS X y Windows lo alientan hasta cierto punto, y Unix siempre lo ha respaldado, aunque con herramientas comoxargshacerlo más difícil), entonces debería incluir nombres de archivos entre comillas dobles cada vez que los use también. A menos que esté a cargo del valor (por ejemplo, durante el manejo de opciones, y establezca la variable a 'no' al inicio y 'sí' cuando se incluye un indicador en la línea de comando), entonces no es seguro usar formas de variables sin comillas hasta que haya demostrado que son seguros, 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 personajes de los que preocuparse también: los backticks también pueden ser bastante desagradables, por ejemplo).

Respondida el 07/10/2008 a las 21:00
fuente por usuario

votos
3

Recomiendo su lugar:

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

ya que elimina la fea x, y aún así se soluciona el problema mencionado por https://stackoverflow.com/a/174288/895245 que $SHELL_VARpuede comenzar con -y ser leído como una opción.

Respondida el 30/06/2016 a las 01:04
fuente por usuario

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more