¿Por qué las comparaciones de script de shell a menudo usan x $ VAR = xyes?
Pregunta
Veo esto a menudo en los scripts de compilación de proyectos que usan autotools (autoconf, automake). Cuando alguien quiere verificar el valor de una variable de shell, con frecuencia usa este lenguaje:
if test "x$SHELL_VAR" = "xyes"; then
...
¿Cuál es la ventaja de esto al simplemente verificar el valor de esta manera?
if test $SHELL_VAR = "yes"; then
...
Pienso que debe haber alguna razón por la que veo esto tan a menudo, pero no puedo entender qué es.
Solución
Si está utilizando un shell que hace la sustitución simple y la variable SHELL_VAR
no existe (o está en blanco), entonces debe tener cuidado con el borde casos. 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 para test
ha 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 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 frontal evita que se detecte un guión inicial como una opción para probar
.
Ten en cuenta que esto depende del shell. Algunos shells ( csh
para uno, creo) se quejarán amargamente si la variable de entorno no existe en lugar de simplemente devolver una cadena vacía).
Otros consejos
La otra razón por la que nadie más ha mencionado aún está relacionada con el procesamiento de opciones. Si 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 frontal evita que un guión inicial cause problemas.
Tienes que estar buscando shells realmente antiguos para encontrar uno donde el comando de prueba no tenga soporte para -n
o -z
; el comando test
de la Versión 7 (1978) los incluyó. No es del todo irrelevante: algunas cosas de la Versión 6 de UNIX se escaparon a BSD, pero en estos días, sería muy difícil encontrar algo tan antiguo en el uso actual.
No usar comillas dobles alrededor de 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 admitido, aunque herramientas como xargs
lo dificultan), entonces debería incluya los nombres de los archivos entre comillas dobles cada vez que los use también. A menos que esté a cargo del valor (p. Ej., Durante el manejo de opciones, y establezca la variable en 'no' al inicio y 'sí' cuando se incluya un indicador en la línea de comando), no es seguro usar formas de variables sin comillas. hasta que haya demostrado su seguridad, y 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 también hay otros personajes de los que preocuparse: las comillas invertidas pueden ser bastante desagradables, por ejemplo).
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 " $ cadena " -o " $ a " = " $ b " ] puede causar un error con algunas versiones de Bash si $ string está vacío. La forma segura es añadir un carácter extra a posiblemente variables vacías, [" x $ cadena " ! = x -o " x $ a " = " x $ b " ] (las " x's " cancelan).
Segundo, en otros shells que 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 apunta a la portabilidad en varios entornos UNIX en los que no puede estar seguro de que el shell predeterminado sea Bash y de que sea compatible con la condición de prueba -z, es más seguro usar el formulario si [" x $ SOME_VAR " = " x " ] ya que siempre tendrá el efecto deseado. Esencialmente, este es un viejo truco de shell scripting para encontrar una variable vacía, y todavía se usa hoy en día para la compatibilidad con versiones anteriores a pesar de que hay métodos más limpios disponibles.
Recomiendo en su lugar:
if test "yes" = "$SHELL_VAR"; then
ya que elimina el feo x
, y aún resuelve el problema mencionado por https: // stackoverflow.com/a/174288/895245 que $ SHELL_VAR
puede comenzar con -
y puede leerse como una opción.
Creo que se debe 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 normalmente debería funcionar
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 quejan, 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.
Si no haces el " x $ SHELL_VAR " cosa, entonces si $ SHELL_VAR no está definido, aparece un error sobre " = " no ser un operador monádico o algo así.
Solía ??hacer eso en DOS cuando SHELL_VAR podría no estar definido.