tenga la costumbre de usar [-z "$ mystr"] en lugar de [-z $ mystr]
Charles Duffy el
¿Cómo hacer lo inverso? Quiero decir, cuando la cadena no es nula
abre el camino el
1
@flow: ¿Qué pasa?[ -n "${VAR+x}"] && echo not null
Lekensteyn
1
@CharlesDuffy He leído esto antes en muchos recursos en línea. ¿Por qué se prefiere esto?
incipiente
66
@Ayos porque si no tiene comillas, el contenido de la variable se divide en cadenas y se engloba; si es una cadena vacía, se convierte en [ -z ]lugar de [ -z "" ]; si tiene espacios, se convierte en [ -z "my" "test" ]lugar de [ -z my test ]; y si es así [ -z * ], *se reemplaza por los nombres de los archivos en su directorio.
Charles Duffy
Respuestas:
138
Creo que la respuesta que busca está implícita (si no se indica) en la respuesta de Vinko , aunque no se explica simplemente. Para distinguir si VAR está configurado pero vacío o no, puede usar:
if[-z "${VAR+xxx}"];then echo VAR is not set at all;fiif[-z "$VAR"]&&["${VAR+xxx}"="xxx"];then echo VAR is set but empty;fi
Probablemente pueda combinar las dos pruebas en la segunda línea en una con:
if[-z "$VAR"-a "${VAR+xxx}"="xxx"];then echo VAR is set but empty;fi
Sin embargo, si lee la documentación de Autoconf, encontrará que no recomiendan combinar términos con ' -a' y recomiendan usar pruebas simples separadas combinadas con &&. No he encontrado un sistema donde haya un problema; eso no significa que no existían (pero probablemente son extremadamente raros en estos días, incluso si no fueran tan raros en el pasado distante).
Recientemente me preguntaron por correo electrónico sobre esta respuesta con la pregunta:
Usas dos pruebas, y entiendo bien la segunda, pero no la primera. Más precisamente, no entiendo la necesidad de expansión variable
if[-z "${VAR+xxx}"];then echo VAR is not set at all;fi
¿No lograría esto lo mismo?
if[-z "${VAR}"];then echo VAR is not set at all;fi
Pregunta justa: la respuesta es 'No, su alternativa más simple no hace lo mismo'.
Supongamos que escribo esto antes de su prueba:
VAR=
Su prueba dirá "VAR no está configurado en absoluto", pero el mío dirá (por implicación porque no tiene eco) "VAR está configurado pero su valor puede estar vacío". Prueba este script:
(
unset VARif[-z "${VAR+xxx}"];then echo JL:1 VAR is not set at all;fiif[-z "${VAR}"];then echo MP:1 VAR is not set at all;fi
VAR=if[-z "${VAR+xxx}"];then echo JL:2 VAR is not set at all;fiif[-z "${VAR}"];then echo MP:2 VAR is not set at all;fi)
El resultado es:
JL:1 VAR is not set at all
MP:1 VAR is not set at all
MP:2 VAR is not set at all
En el segundo par de pruebas, la variable se establece, pero se establece en el valor vacío. Esta es la distinción que hacen las anotaciones ${VAR=value}y ${VAR:=value}. Lo mismo para ${VAR-value}y ${VAR:-value}, y ${VAR+value}y ${VAR:+value}, y así sucesivamente.
Como Gili señala en su respuesta , si corres bashcon la set -o nounsetopción, entonces la respuesta básica anterior falla unbound variable. Se remedia fácilmente:
if[-z "${VAR+xxx}"];then echo VAR is not set at all;fiif[-z "${VAR-}"]&&["${VAR+xxx}"="xxx"];then echo VAR is set but empty;fi
O puede cancelar la set -o nounsetopción con set +u(que set -ues equivalente a set -o nounset).
El único problema que tengo con esta respuesta es que cumple su tarea de una manera bastante indirecta y poco clara.
Suiza el
3
Para aquellos que desean buscar la descripción de lo que significa lo anterior en la página de manual de bash, busque la sección "Expansión de parámetros" y luego este texto: "Cuando no se realiza la expansión de subcadenas, utilizando los formularios documentados a continuación, las pruebas de bash para un parámetro que no está establecido o es nulo ['nulo' significa la cadena vacía]. Omitir los dos puntos resulta en una prueba solo para un parámetro que no está establecido (...) $ {parámetro: + palabra}: Usar valor alternativo. Si el parámetro es nulo o no establecido, nada se sustituye, de lo contrario, la expansión de la palabra se sustituye ".
David Tonhofer
2
@Swiss Esto no es claro ni indirecto, sino idiomático. Quizás para un programador que no esté familiarizado ${+}y ${-}no esté claro, pero la familiaridad con esas construcciones es esencial si se quiere ser un usuario competente del shell.
Hice muchas pruebas sobre esto; porque los resultados en mis guiones fueron inconsistentes. Sugiero que la gente busque el [ -v VAR ]enfoque " " con bash -s v4.2 y más allá .
-z también funciona para variables indefinidas. Para distinguir entre un indefinido y uno definido, usaría las cosas enumeradas aquí o, con explicaciones más claras, aquí .
La forma más limpia es usar la expansión como en estos ejemplos. Para obtener todas sus opciones, consulte la sección Expansión de parámetros del manual.
Palabra alternativa:
~$ unset FOO~$ if test ${FOO+defined};then echo "DEFINED";fi~$ FOO=""~$ if test ${FOO+defined};then echo "DEFINED";fi
DEFINED
Valor por defecto:
~$ FOO=""~$ if test "${FOO-default value}";then echo "UNDEFINED";fi~$ unset FOO~$ if test "${FOO-default value}";then echo "UNDEFINED";fi
UNDEFINED
Por supuesto, usaría uno de estos de manera diferente, colocando el valor que desea en lugar del 'valor predeterminado' y usando la expansión directamente, si corresponde.
Pero quiero distinguir si la cadena es "" o no se ha definido nunca. ¿Es eso posible?
Setjmp
1
esta respuesta no dice cómo distinguir entre los dos casos; siga el enlace de preguntas frecuentes de bash que proporciona para más discusión.
Charles Duffy
1
Agregué
2
Busque "Expansión de parámetros" en la página de manual de bash para ver todos estos "trucos". Por ejemplo, $ {foo: -default} para usar un valor predeterminado, $ {foo: = default} para asignar el valor predeterminado, $ {foo:? Mensaje de error} para mostrar un mensaje de error si foo no está configurado, etc.
Jouni K Seppänen
18
Guía avanzada de secuencias de comandos bash, 10.2. Sustitución de parámetros:
$ {var + blahblah}: si se define var, 'blahblah' se sustituye por la expresión, de lo contrario se sustituye nulo
$ {var-blahblah}: si se define var, se sustituye a sí mismo, de lo contrario, 'blahblah' se sustituye
$ {var? blahblah}: si se define var, se sustituye; de lo contrario, la función existe con 'blahblah' como mensaje de error.
para basar la lógica de su programa en si la variable $ mystr está definida o no, puede hacer lo siguiente:
isdefined=0
${mystr+ export isdefined=1}
ahora, si isdefined = 0, entonces la variable no estaba definida, si isdefined = 1, la variable estaba definida
Esta forma de verificar las variables es mejor que la respuesta anterior porque es más elegante, legible, y si su shell bash se configuró para error en el uso de variables indefinidas (set -u), el script terminará prematuramente.
Otras cosas útiles:
tener un valor predeterminado de 7 asignado a $ mystr si no estaba definido, y dejarlo intacto de lo contrario:
mystr=${mystr-7}
para imprimir un mensaje de error y salir de la función si la variable no está definida:
: ${mystr? not defined}
Tenga en cuenta aquí que usé ':' para no tener el contenido de $ mystr ejecutado como un comando en caso de que esté definido.
No sabía sobre el? sintaxis para las variables BASH. Esa es una buena captura.
Suiza el
Esto funciona también con referencias variables indirectas. Esto pasa: var= ; varname=var ; : ${!varname?not defined}y esto termina: varname=var ; : ${!varname?not defined}. Pero es un buen hábito de usar set -uque hace lo mismo mucho más fácil.
ceving
11
Un resumen de las pruebas.
[-n "$var"]&& echo "var is set and not empty"[-z "$var"]&& echo "var is unset or empty"["${var+x}"="x"]&& echo "var is set"# may or may not be empty[-n "${var+x}"]&& echo "var is set"# may or may not be empty[-z "${var+x}"]&& echo "var is unset"[-z "${var-x}"]&& echo "var is set and empty"
Buena práctica en bash. A veces, las rutas de archivo tienen espacios o para proteger contra la inyección de comandos
k107
1
Creo que con ${var+x}esto no es necesario, excepto si se permite que los nombres de variables tengan espacios en ellos.
Anne van Rossum
No solo buenas prácticas; se requiere con [obtener resultados correctos. Si no varestá configurado o está vacío, se [ -n $var ]reduce a [ -n ]cuál tiene el estado de salida 0, mientras que [ -n "$var" ]tiene el estado de salida esperado (y correcto) de 1.
if[-n "${VAR-}"];then
echo "VAR is set and is not empty"elif["${VAR+DEFINED_BUT_EMPTY}"="DEFINED_BUT_EMPTY"];then
echo "VAR is set, but empty"else
echo "VAR is not set"fi
No puedo encontrar esto en ninguna de las páginas de manual (para bash o test) pero funciona para mí. ¿Alguna idea de qué versiones se agregó?
Ash Berlin-Taylor
1
Está documentado en Expresiones condicionales , referenciadas por el testoperador en el final de la sección Bourne Shell Builtins . No sé cuándo se agregó, pero no está en la versión de Apple bash(basada en bash3.2.51), por lo que probablemente sea una característica 4.x.
Jonathan Leffler
1
Esto no funciona para mí GNU bash, version 4.1.2(1)-release (x86_64-redhat-linux-gnu). El error es -bash: [: -v: unary operator expected. Según un comentario aquí , se requiere como mínimo bash 4.2 para funcionar.
Acumenus
Gracias por comprobar cuándo estuvo disponible. Lo había usado antes en varios sistemas, pero de repente no funcionó. Vine aquí por curiosidad y sí, por supuesto, el bash en este sistema es un 3.x bash.
no arrojar esta bicicleta aún más lejos, pero quería agregar
shopt -s -o nounset
es algo que podría agregar a la parte superior de una secuencia de comandos, lo que generará un error si las variables no se declaran en ninguna parte de la secuencia de comandos. El mensaje que vería es unbound variable, pero como otros mencionan, no detectará una cadena vacía o un valor nulo. Para asegurarnos de que cualquier valor individual no esté vacío, podemos probar una variable a medida que se expande ${mystr:?}, también conocida como expansión de signo de dólar, que generaría un error parameter null or not set.
Esto se puede aplicar seleccionando visualmente las líneas relevantes y luego escribiendo :. La barra de comandos se rellena previamente con :'<,'>. Pegue el comando anterior y presione enter.
Probado en esta versión de Vim:
VIM -ViIMproved7.3(2010Aug15, compiled Aug22201515:38:58)Compiled by root@apple.com
Los usuarios de Windows pueden querer diferentes finales de línea.
Escribir una función no es una mala idea; invocar grepes horrible. Tenga en cuenta que bashadmite ${!var_name}como una forma de obtener el valor de una variable cuyo nombre se especifica $var_name, por lo que name=value; value=1; echo ${name} ${!name}rinde value 1.
Jonathan Leffler
0
Una versión más corta para probar una variable indefinida puede ser simplemente:
conjunto de llamadas sin ningún argumento ... genera todos los vars definidos ...
los últimos en la lista serían los definidos en su script ...
para que pueda canalizar su salida a algo que pueda averiguar qué cosas están definidas y cuáles no
No voté en contra de esto, pero es una especie de mazo romper una nuez bastante pequeña.
Jonathan Leffler
De hecho, me gusta esta respuesta mejor que la respuesta aceptada. Está claro lo que se está haciendo en esta respuesta. La respuesta aceptada es torpe como el infierno.
Suiza el
Parece que esta respuesta es más un efecto secundario de la implementación de "set" que algo deseable.
[ -n "${VAR+x}"] && echo not null
[ -z ]
lugar de[ -z "" ]
; si tiene espacios, se convierte en[ -z "my" "test" ]
lugar de[ -z my test ]
; y si es así[ -z * ]
,*
se reemplaza por los nombres de los archivos en su directorio.Respuestas:
Creo que la respuesta que busca está implícita (si no se indica) en la respuesta de Vinko , aunque no se explica simplemente. Para distinguir si VAR está configurado pero vacío o no, puede usar:
Probablemente pueda combinar las dos pruebas en la segunda línea en una con:
Sin embargo, si lee la documentación de Autoconf, encontrará que no recomiendan combinar términos con '
-a
' y recomiendan usar pruebas simples separadas combinadas con&&
. No he encontrado un sistema donde haya un problema; eso no significa que no existían (pero probablemente son extremadamente raros en estos días, incluso si no fueran tan raros en el pasado distante).Puede encontrar los detalles de estas y otras expansiones de parámetros de shell relacionados , el comando
test
o[
y las expresiones condicionales en el manual de Bash.Recientemente me preguntaron por correo electrónico sobre esta respuesta con la pregunta:
Pregunta justa: la respuesta es 'No, su alternativa más simple no hace lo mismo'.
Supongamos que escribo esto antes de su prueba:
Su prueba dirá "VAR no está configurado en absoluto", pero el mío dirá (por implicación porque no tiene eco) "VAR está configurado pero su valor puede estar vacío". Prueba este script:
El resultado es:
En el segundo par de pruebas, la variable se establece, pero se establece en el valor vacío. Esta es la distinción que hacen las anotaciones
${VAR=value}
y${VAR:=value}
. Lo mismo para${VAR-value}
y${VAR:-value}
, y${VAR+value}
y${VAR:+value}
, y así sucesivamente.Como Gili señala en su respuesta , si corres
bash
con laset -o nounset
opción, entonces la respuesta básica anterior fallaunbound variable
. Se remedia fácilmente:O puede cancelar la
set -o nounset
opción conset +u
(queset -u
es equivalente aset -o nounset
).fuente
${+}
y${-}
no esté claro, pero la familiaridad con esas construcciones es esencial si se quiere ser un usuario competente del shell.set -o nounset
habilitado.[ -v VAR ]
enfoque " " con bash -s v4.2 y más allá .-z también funciona para variables indefinidas. Para distinguir entre un indefinido y uno definido, usaría las cosas enumeradas aquí o, con explicaciones más claras, aquí .
La forma más limpia es usar la expansión como en estos ejemplos. Para obtener todas sus opciones, consulte la sección Expansión de parámetros del manual.
Palabra alternativa:
Valor por defecto:
Por supuesto, usaría uno de estos de manera diferente, colocando el valor que desea en lugar del 'valor predeterminado' y usando la expansión directamente, si corresponde.
fuente
Guía avanzada de secuencias de comandos bash, 10.2. Sustitución de parámetros:
para basar la lógica de su programa en si la variable $ mystr está definida o no, puede hacer lo siguiente:
ahora, si isdefined = 0, entonces la variable no estaba definida, si isdefined = 1, la variable estaba definida
Esta forma de verificar las variables es mejor que la respuesta anterior porque es más elegante, legible, y si su shell bash se configuró para error en el uso de variables indefinidas
(set -u)
, el script terminará prematuramente.Otras cosas útiles:
tener un valor predeterminado de 7 asignado a $ mystr si no estaba definido, y dejarlo intacto de lo contrario:
para imprimir un mensaje de error y salir de la función si la variable no está definida:
Tenga en cuenta aquí que usé ':' para no tener el contenido de $ mystr ejecutado como un comando en caso de que esté definido.
fuente
var= ; varname=var ; : ${!varname?not defined}
y esto termina:varname=var ; : ${!varname?not defined}
. Pero es un buen hábito de usarset -u
que hace lo mismo mucho más fácil.Un resumen de las pruebas.
fuente
${var+x}
esto no es necesario, excepto si se permite que los nombres de variables tengan espacios en ellos.[
obtener resultados correctos. Si novar
está configurado o está vacío, se[ -n $var ]
reduce a[ -n ]
cuál tiene el estado de salida 0, mientras que[ -n "$var" ]
tiene el estado de salida esperado (y correcto) de 1.https://stackoverflow.com/a/9824943/14731 contiene una mejor respuesta (una que es más legible y funciona con
set -o nounset
habilitada). Funciona más o menos así:fuente
La forma explícita de verificar una variable que se define sería:
fuente
test
operador en el final de la sección Bourne Shell Builtins . No sé cuándo se agregó, pero no está en la versión de Applebash
(basada enbash
3.2.51), por lo que probablemente sea una característica 4.x.GNU bash, version 4.1.2(1)-release (x86_64-redhat-linux-gnu)
. El error es-bash: [: -v: unary operator expected
. Según un comentario aquí , se requiere como mínimo bash 4.2 para funcionar.otra opción: la expansión "listar índices de matriz" :
el único momento en que esto se expande a la cadena vacía es cuando no
foo
está configurado, por lo que puede verificarlo con la cadena condicional:debería estar disponible en cualquier versión bash> = 3.0
fuente
no arrojar esta bicicleta aún más lejos, pero quería agregar
es algo que podría agregar a la parte superior de una secuencia de comandos, lo que generará un error si las variables no se declaran en ninguna parte de la secuencia de comandos. El mensaje que vería es
unbound variable
, pero como otros mencionan, no detectará una cadena vacía o un valor nulo. Para asegurarnos de que cualquier valor individual no esté vacío, podemos probar una variable a medida que se expande${mystr:?}
, también conocida como expansión de signo de dólar, que generaría un errorparameter null or not set
.fuente
El Manual de referencia de Bash es una fuente autorizada de información sobre bash.
Aquí hay un ejemplo de prueba de una variable para ver si existe:
(De la sección 6.3.2 .)
Tenga en cuenta que el espacio en blanco después de la apertura
[
y antes de la no]
es opcional .Consejos para usuarios de Vim
Tenía un script que tenía varias declaraciones de la siguiente manera:
Pero quería que difirieran a cualquier valor existente. Así que los reescribí para que se vean así:
Pude automatizar esto en vim usando una expresión regular rápida:
Esto se puede aplicar seleccionando visualmente las líneas relevantes y luego escribiendo
:
. La barra de comandos se rellena previamente con:'<,'>
. Pegue el comando anterior y presione enter.Probado en esta versión de Vim:
Los usuarios de Windows pueden querer diferentes finales de línea.
fuente
Esto es lo que creo que es una forma mucho más clara de verificar si se define una variable:
Úselo de la siguiente manera:
fuente
grep
es horrible. Tenga en cuenta quebash
admite${!var_name}
como una forma de obtener el valor de una variable cuyo nombre se especifica$var_name
, por lo quename=value; value=1; echo ${name} ${!name}
rindevalue 1
.Una versión más corta para probar una variable indefinida puede ser simplemente:
fuente
conjunto de llamadas sin ningún argumento ... genera todos los vars definidos ...
los últimos en la lista serían los definidos en su script ...
para que pueda canalizar su salida a algo que pueda averiguar qué cosas están definidas y cuáles no
fuente