¿Por qué no usar múltiples comandos con un || o && trabajo condicional?

12

Esto funciona en un indicador de shell (bash, dash):

[ -z "" ] && echo A || echo B
A

Sin embargo, estoy tratando de escribir un script de shell POSIX , comienza así:

#!/bin/sh

[ "${#}" -eq 1 ] || echo "Invalid number of arguments, expected one."; exit 1

readonly raw_input_string=${1}

[ -z "${raw_input_string}" ] && echo "The given argument is empty."; exit 1

Y no sé por qué, pero no recibo el mensaje :

El argumento dado está vacío.

si llamo al script así:

./test_empty_argument ""

¿Porqué es eso?

LinuxSecurityFreak
fuente
55
Consulte ¿Cómo puedo probar si una variable está vacía o contiene solo espacios? para conocer las formas de probar si una variable está vacía, sin establecer o solo contiene espacios en blanco. El problema en esta pregunta no tiene nada que ver con eso.
ilkkachu
1
Solo useif [ X”” = X”$var” ] ; then echo isempty ; fi
user2497
3
@ user2497 No hay ninguna razón para usar eso en ningún shell lanzado en los últimos 20 años. Esa es una solución para los viejos depósitos con errores.
chepner el
@chepner ¿Entonces no es una solución válida? Algo más debe ser utilizado?
user2497
66
[ "" = "$var" ]funcionaría bien; una cadena vacía entrecomillada no se eliminará de la lista de argumentos de [. Pero eso tampoco es necesario, porque [ -z "$var" ] también funciona bien.
chepner

Respuestas:

37

Tenga en cuenta que su línea

[ "${#}" -eq 1 ] || echo "Invalid number of arguments, expected one."; exit 1

esto es lo mismo que

[ "${#}" -eq 1 ] || echo "Invalid number of arguments, expected one."
exit 1

(un no citado ;puede, en la mayoría de los casos, ser reemplazado por un carácter de nueva línea)

Esto significa que la exit 1declaración siempre se ejecuta independientemente de cuántos argumentos se pasaron al script. Esto a su vez significa que el mensaje The given argument is empty.nunca tendrá la posibilidad de imprimirse.

Para ejecutar más de una declaración después de una prueba usando la "sintaxis de cortocircuito", agrupe las declaraciones en { ...; }. La alternativa es usar una ifdeclaración adecuada (que, en mi humilde opinión, se ve más limpia en un script):

if [ "$#" -ne 1 ]; then
    echo 'Invalid number of arguments, expected one.' >&2
    exit 1
fi

Tiene el mismo problema con su segunda prueba.


Respecto a

[ -z "" ] && echo A || echo B

Esto funcionaría para el ejemplo dado, pero el genérico

some-test && command1 || command2

sería no ser el mismo que

if some-test; then
    command1
else
    command2
fi

En cambio, es más como

if ! { some-test && command1; }; then
    command2
fi

o

if some-test && command1; then
    :
else
    command2
fi

Es decir, si falla la prueba o el primer comando, se ejecuta el segundo comando, lo que significa que tiene el potencial de ejecutar las tres declaraciones involucradas.

Kusalananda
fuente
18

Esta:

[ "${#}" -eq 1 ] || echo "Invalid number of arguments, expected one."; exit 1

no es:

[ "${#}" -eq 1 ] || { echo "Invalid number of arguments, expected one."; exit 1; }

Pero en cambio es:

{ [ "${#}" -eq 1 ] || echo "Invalid number of arguments, expected one."; } 
exit 1

Su script se está cerrando independientemente de cuántos argumentos le haya pasado.

muru
fuente
8

Una forma de hacerlo más legible es definir una diefunción (a la perl) como:

die() {
  printf >&2 '%s\n' "$@"
  exit 1
}

# then:

[ "$#" -eq 1 ] || die "Expected one argument, got $#"

[ -n "$1" ] || die "Empty argument not supported"

Puede agregar más campanas y silbatos como colores, prefijo, número de línea ... si es necesario.

Stéphane Chazelas
fuente
En la práctica, ¿alguna vez llamas a tu diefunción con múltiples argumentos? (Si es así, ¿puede dar un ejemplo?) Uso una diefunción casi idéntica , pero uso "$*"en su lugar, ¿qué puede ser más de lo que pretende?
jrw32982 apoya a Monica el
3
El valor de "$@"es que permite mensajes de varias líneas sin necesidad de agregar nuevas líneas literales.
Charles Duffy el
1
@ jrw32982, usar "$*"para unir args con espacios también significa que debe configurar $IFSSPC para que funcione en todos los contextos, incluidos aquellos en los que $IFSse ha modificado. Alternativamente con ksh/ zsh, puede usar print -r -- "$@"o echo -E - "$@"en zsh.
Stéphane Chazelas
@CharlesDuffy Sí, pero nunca lo he visto en el contexto de una diefunción de tipo. Lo que pregunto es: en la práctica, ¿alguna vez has visto a alguien escribir die "unable to blah:" "some error", con el fin de obtener un mensaje de error de 2 líneas?
jrw32982 es compatible con Monica el
@ StéphaneChazelas Buen punto. Entonces (en mi formulación) debería ser die() { IFS=" "; printf >&2 "%s\n" "$*"; exit 1; }. ¿Alguna vez ha utilizado este tipo de diefunción para printfgenerar un mensaje de error de varias líneas al pasar varios argumentos? ¿O solo pasa un argumento para dieque solo agregue una nueva línea a su salida?
jrw32982 es compatible con Monica el
-1

A menudo he visto esto como una prueba para una cadena vacía:

if [ "x$foo" = "x" ]; then ...
wef
fuente
Debería haber sido "=" - arreglado.
wef
3
Esa práctica es literalmente de la década de 1970. No hay razón alguna para usarlo con cualquier cáscara que es compatible con el estándar sh POSIX 1992 (siempre y cuando citando se utilizan y funcionalidad ahora obsoleto como correcta -a, -o, (y )como derectives para contar testcombinar múltiples operaciones en una sola invocación se evitan; vea los marcadores OB en pubs.opengroup.org/onlinepubs/9699919799/utilities/test.html ).
Charles Duffy el
Las unidades que no son de Linux se enviaron con la 'sh' original hasta bien entrada la década de los 90, tal vez aún lo hagan. En mi trabajo en ese momento, tuve que escribir scripts de instalación portátiles y usé esta construcción. Acabo de ver los scripts de instalación que NVidia envía para Linux y todavía usan esta construcción.
wef
NVidia puede usarlo, pero eso no quiere decir que tengan alguna justificación técnica para hacerlo; El desarrollo del culto de carga en UNIX comercial es tristemente frecuente. Incluso Heirloom Bourne no tiene el error en cuestión, por lo que la base de código SunOS (que es el último UNIX comercial en enviar un no POSIX /bin/shy el predecesor inmediato de Heirloom Bourne) tampoco lo tenía.
Charles Duffy el
1
No uso sombrero, por lo que no puedo prometer que publique un video de YouTube comiéndolo si alguien presenta un shell publicado en un UNIX comercial con este error después de 1990 ... pero si lo hiciera, sería tentador . :)
Charles Duffy el