2011-07-27 7 views
70

Sé que puedo comprobar una cadena vacía en Bash con -z así:prueba del golpe de cadena vacía con x ""

if [[ -z $myvar ]]; then do_stuff; fi 

pero veo mucho código escrito como:

if [[ X"" = X"$myvar" ]]; then do_stuff; fi 

¿Es ese método más portátil? ¿Es simplemente un cruft histórico desde antes de los días de -z? ¿Es para shells POSIX (aunque lo he visto usado en scripts dirigidos a bash)? Listo para mi lección de historia/portabilidad.


La misma pregunta se hizo en el servidor falla como How to determine if a bash variable is empty? pero nadie se ofreció a una explicación de por qué ve el código con las cosas X"".

+1

Gran pregunta por cierto. Esta publicación tiene una buena respuesta [serverfault post] (http://serverfault.com/questions/7503/how-to-determine-if-a-bash-variable-is-empty) – chrislovecnm

Respuesta

110

Fundamentalmente, porque en tiempos pasados, el comportamiento de test era más complejo y no se definía uniformemente en diferentes sistemas (por lo que el código portátil debía escribirse cuidadosamente para evitar construcciones no portátiles).

En particular, antes de test era una cáscara incorporado, fue un ejecutable independiente (y observar que MacOS X todavía tiene /bin/test y /bin/[ como ejecutables). Cuando ese es el caso, por escrito:

if [ -z $variable ] 

cuando $variable estaba vacío sería invocar el programa de prueba a través de su alias [ con 3 argumentos:

argv[0] = "[" 
argv[1] = "-z" 
argv[2] = "]" 

porque la variable estaba vacío, así que no había nada para expandir . Por lo tanto, la manera segura de escribir el código era:

if [ -z "$variable" ] 

Esto funciona de forma fiable, pasando 4 argumentos a la test ejecutable. De acuerdo, el programa de prueba ha sido incorporado a la mayoría de los proyectiles durante décadas, pero los equipos viejos mueren mucho, y también lo hacen las buenas prácticas aprendidas incluso más tiempo atrás.

El otro problema resuelto por el prefijo X fue lo que sucedió si las variables incluyen guiones principales o contienen iguales u otros comparadores. Considere (un buen ejemplo no necesitan desesperadamente):

x="-z" 
if [ $x -eq 0 ] 

es que una prueba de cadena vacía con un argumento callejero (errónea), o una prueba de la igualdad numérica con un primer argumento no numérico? Diferentes sistemas proporcionaron diferentes respuestas antes de que POSIX estandarizara el comportamiento, alrededor de 1990.Por lo tanto, la manera segura de hacer frente a esto fue:

if [ "X$x" = "X0" ] 

o (menos por lo general, en mi experiencia, pero completamente de forma equivalente):

if [ X"$x" = X"0" ] 

Fue todos los casos extremos como éste, atado con la posibilidad de que la prueba sea un ejecutable independiente, eso significa que el código de shell portátil todavía usa comillas dobles más copiosamente de lo que las conchas modernas realmente requieren, y la notación de X-prefix se usó para asegurar que las cosas no pudieran malinterpretarse.

+5

La mayoría, tal vez todas, como las de Unix los sistemas todavía tienen ejecutables de "prueba" y "[" (en Ubuntu, están en/usr/bin, no en/bin). Esto les permite ser utilizados a partir de otras cosas que no sean scripts de shell. (Consulte la documentación de su sistema; es probable que el comando se comporte sutilmente de manera diferente que el shell incorporado, que en sí mismo puede variar de un shell a otro). –

9

Ah, esta es una de mis preguntas y respuestas preferidas, porque se me ocurrió la respuesta solo de pensarlo. El X se establece en caso de que la cadena comience con -, que se puede tomar como una bandera para la prueba. Poner un X antes simplemente elimina ese caso, y la comparación todavía puede mantenerse.

También me gusta porque este tipo de truco es casi una herencia del pasado, los tiempos más antiguos de la informática, y lo encuentras cuando intentas leer algunos de los scripts de shell más portátiles (autoconf, configurar, etc. .)

+1

tiene sentido ... aún así, debe ser histórico porque 'myvar =" - somval "; si [-z $ myvar]; luego echo "cero"; fi' no da salida a "cero" (o vomita) en mi caparazón (bash) ... – mgalgs

+1

@mitch: sí, tal vez no fallará exactamente en '-z', pero se usa (fuera de la costumbre, Puedo pensar) solo como un marcador para todas las comparaciones de cuerdas. –

+1

Todas las versiones de 'test' que probé (bash built-in también) analizan correctamente comandos como' [-z = -z] ',' []! = [] ', Etc. –