unix - ¿Por qué las comparaciones de scripts de shell a menudo usan x $ VAR=xyes?
autotools (7)
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.
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.
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.
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 mirar conchas realmente antiguas para encontrar una donde el comando de prueba no tenga soporte para -n
o -z
; el comando de test
la Versión 7 (1978) test
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 las herramientas como xargs
hacen más difícil), debe incluir nombres de archivos entre comillas dobles cada vez que los uses 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).
Recomiendo en su lugar:
if test "yes" = "$SHELL_VAR"; then
ya que elimina la fea x
, y aún resuelve el problema mencionado por https://.com/a/174288/895245 que $SHELL_VAR
puede comenzar y se puede leer como una opción.
Si está utilizando un shell que hace una sustitución simple y la variable SHELL_VAR
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 para test
se ha perdido. 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 para el comando de test
.
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 $1
tiene el valor -n
o -z
o cualquier otra opción válida para el comando de test
, la sintaxis es ambigua. La x
en la parte delantera evita que se recoja un guion delantero como una opción para test
.
Tenga en cuenta que esto depende del caparazón. Algunas conchas ( csh
por una, creo) se quejarán amargamente si la variable de entorno no existe en lugar de simplemente devolver una cadena vacía).
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í.
Solía hacer eso en DOS cuando el SHELL_VAR podría estar indefinido.