Verifique la existencia del argumento de entrada en un script de shell Bash

1341

Necesito verificar la existencia de un argumento de entrada. Tengo el siguiente script

if [ "$1" -gt "-1" ]
  then echo hi
fi

yo obtengo

[: : integer expression expected

¿Cómo verifico primero el argumento de entrada1 para ver si existe?

usuario775187
fuente

Respuestas:

2334

Está:

if [ $# -eq 0 ]
  then
    echo "No arguments supplied"
fi

La $#variable le indicará la cantidad de argumentos de entrada que se pasó el script.

O puede verificar si un argumento es una cadena vacía o no:

if [ -z "$1" ]
  then
    echo "No argument supplied"
fi

El -zconmutador probará si la expansión de "$ 1" es una cadena nula o no. Si es una cadena nula, se ejecuta el cuerpo.

foxis
fuente
62
Me gusta hacerlo de esta manera, en sintaxis concisa y aún POSIX aceptable. [ -z "$1" ] && echo "No argument supplied" Prefiero frases sencillas, ya que son más fáciles para mí; y también es más rápido verificar el valor de salida, en comparación con el usoif
JM Becker
168
Probablemente desee agregar un exit 1al final de sus ecos dentro del bloque if cuando se requiera el argumento para que el script funcione. Obvio, pero vale la pena señalar la integridad.
msanford
16
Es posible, aunque raramente útil, que el primer argumento se inicialice pero esté vacío; programname "" secondarg third. La $#verificación verifica sin ambigüedades el número de argumentos.
tripleee
39
Para un novato, especialmente alguien que proviene de un entorno sin secuencias de comandos, también es importante mencionar algunas peculiaridades sobre estas cosas. También podría haber mencionado que necesitamos un espacio después de la llave de apertura y cierre. De lo contrario, las cosas no funcionan. Yo mismo soy un novato de secuencias de comandos (vengo de fondo C) y lo encontré por las malas. Fue solo cuando decidí copiar todo "tal cual" que las cosas funcionaron para mí. Fue entonces cuando me di cuenta de que tenía que dejar un espacio después del corsé de apertura y antes del cierre.
HighOnMeat
72
y para args opcionalesif [ ! -z "$1" ]; then ...
gcb
347

Es mejor demostrar de esta manera

if [[ $# -eq 0 ]] ; then
    echo 'some message'
    exit 1
fi

Normalmente necesita salir si tiene muy pocos argumentos.

Val
fuente
72
No, no lo es: esto tiene exit 1lo que generalmente desea, y utiliza la [[ ]]prueba que (iirc) suele ser más razonable. Entonces, para las personas que copian y pegan código a ciegas, esta es la mejor respuesta.
dshepherd el
42
Para saber más sobre la diferencia entre [] y [[]] ver stackoverflow.com/questions/3427872/…
Sebastián Grignoli
104

En algunos casos, debe verificar si el usuario pasó un argumento al script y, de lo contrario, volver a un valor predeterminado. Como en el guión a continuación:

scale=${2:-1}
emulator @$1 -scale $scale

Aquí, si el usuario no ha pasado scalecomo segundo parámetro, ejecuto el emulador de Android -scale 1de forma predeterminada. ${varname:-word}Es un operador de expansión. También hay otros operadores de expansión:

  • ${varname:=word}que establece lo indefinido en varnamelugar de devolver el wordvalor;
  • ${varname:?message}que devuelve varnamesi está definido y no es nulo o imprime messagey aborta el script (como el primer ejemplo);
  • ${varname:+word}que devuelve wordsolo si varnameestá definido y no es nulo; devuelve nulo de lo contrario.
Aleks N.
fuente
1
El ejemplo anterior parece usar ${varname?message}. ¿El extra es :un error tipográfico o cambia el comportamiento?
Eki
66
Eki, el ":" es un comando incorporado y abreviatura de / bin / true en este ejemplo. Representa un comando de no hacer nada que básicamente ignora los argumentos que se proporcionan. Es esencial en esta prueba para evitar que el intérprete intente ejecutar el contenido de "$ varname" (que ciertamente NO desea que ocurra). También vale la pena señalar; Puede probar tantas variables con este método como desee. Y todo con mensajes de error específicos. es decir: ${1?"First argument is null"} ${2?"Please provide more than 1 argument"}
user.friendly
48

Tratar:

 #!/bin/bash
 if [ "$#" -eq  "0" ]
   then
     echo "No arguments supplied"
 else
     echo "Hello world"
 fi
Ranjithkumar T
fuente
44
¿Por qué necesita comillas dobles para $#y 0?
usuario13107
1
No hay problema si usamos sin comillas dobles como $ # y 0
Ranjithkumar T
en Windows, mingw, este es el único camino a seguir.
Lajos Meszaros
2
Esta respuesta proporciona un excelente punto de partida para un script que acabo de hacer. Gracias por mostrarnos elsetambién.
Chris K
2
@ user13107 las variables con comillas dobles en bash evitan el engorde (es decir, expandir nombres de archivos como foo*) y la división de palabras (es decir, dividir el contenido si el valor contiene espacios en blanco). En este caso no es necesario citar $#porque ambos casos no se aplican. Citar el 0tampoco es necesario, pero algunas personas prefieren citar valores ya que en realidad son cadenas y eso lo hace más explícito.
Dennis
39

Otra forma de detectar si se pasaron argumentos al script:

((!$#)) && echo No arguments supplied!

Tenga en cuenta que (( expr ))hace que la expresión se evalúe según las reglas de Shell Arithmetic .

Para salir en ausencia de argumentos, se puede decir:

((!$#)) && echo No arguments supplied! && exit 1

Otra forma (análoga) de decir lo anterior sería:

let $# || echo No arguments supplied

let $# || { echo No arguments supplied; exit 1; }  # Exit if no arguments!

help let dice:

let: let arg [arg ...]

  Evaluate arithmetic expressions.

  ...

  Exit Status:
  If the last ARG evaluates to 0, let returns 1; let returns 0 otherwise.
devnull
fuente
2
-1 este podría ser el peor método si se valida la existencia de un argumento ... además, puede desencadenar la sustitución del historial y potencialmente hacer cosas malas.
user.friendly
2
en lugar de lo exitque mata mi proceso zsh, uso lo returnque no lo mata
Timo
¿Por qué ((!$#))desencadenaría la sustitución de la historia?
Zhro
25

A menudo uso este fragmento para secuencias de comandos simples:

#!/bin/bash

if [ -z "$1" ]; then
    echo -e "\nPlease call '$0 <argument>' to run this command!\n"
    exit 1
fi
f2cx
fuente
1
Entonces, ¿esto se debe usar si solo necesita un argumento?
Danijel
21

Solo porque hay un punto más básico para señalar, agregaré que simplemente puede probar que su cadena es nula:

if [ "$1" ]; then
  echo yes
else
  echo no
fi

Del mismo modo, si está esperando un recuento de argumentos, solo pruebe su último:

if [ "$3" ]; then
  echo has args correct or not
else
  echo fixme
fi

y así sucesivamente con cualquier arg o var

seorfatos
fuente
4

Si desea verificar si el argumento existe, puede verificar si el número de argumentos es mayor o igual que su número de argumento objetivo.

El siguiente script demuestra cómo funciona esto

test.sh

#!/usr/bin/env bash

if [ $# -ge 3 ]
then
  echo script has at least 3 arguments
fi

produce la siguiente salida

$ ./test.sh
~
$ ./test.sh 1
~
$ ./test.sh 1 2
~
$ ./test.sh 1 2 3
script has at least 3 arguments
$ ./test.sh 1 2 3 4
script has at least 3 arguments
Brad Parks
fuente
3

Como un pequeño recordatorio, los operadores numéricos de los análisis en Bash sólo funcionan en números enteros ( -eq, -lt, -ge, etc.)

Me gusta asegurarme de que mis $ vars son ints por

var=$(( var + 0 ))

antes de probarlos, solo para defenderme del error "[: se requiere argumento entero".

Cwissy
fuente
1
Buen truco, pero tenga en cuenta: debido a la incapacidad de bash para manejar flotantes en aritmética, este método puede causar un error de sintaxis y devolver un valor distinto de cero, lo que sería un obstáculo cuando errexit está habilitado. var=$(printf "%.0f" "$var")puede manejar flotantes pero sufre de la salida que no es cero cuando se le da una cadena. Si no te importa un awk, ello utilizo método parece ser el más robusto de hacer cumplir un entero: var=$(<<<"$var" awk '{printf "%.0f", $0}'). Si var no está configurado, el valor predeterminado es "0". Si var es un flotante, se redondea al entero más cercano. Los valores negativos también están bien para usar.
user.friendly
0

validación de una función bash de línea

myFunction() {

    : ${1?"forgot to supply an argument"}
    if [ "$1" -gt "-1" ]; then
        echo hi
    fi

}

agregar nombre de función y uso

myFunction() {

    : ${1?"forgot to supply an argument ${FUNCNAME[0]}() Usage:  ${FUNCNAME[0]} some_integer"}
    if [ "$1" -gt "-1" ]; then
        echo hi
    fi

}

agregar validación para verificar si es entero

para agregar validación adicional, por ejemplo para verificar si el argumento pasado es un número entero, modifique la línea de validación para llamar a una función de validación:

: ${1?"forgot to supply an argument ${FUNCNAME[0]}() Usage:  ${FUNCNAME[0]} some_integer"} && validateIntegers $1 || die "Must supply an integer!"

luego, construya una función de validación que valide el argumento, devolviendo 0 en caso de éxito, 1 en caso de error y una función de matriz que anule el script en caso de error

validateIntegers() {

    if ! [[ "$1" =~ ^[0-9]+$ ]]; then
        return 1 # failure
    fi
    return 0 #success

}

die() { echo "$*" 1>&2 ; exit 1; }

Aún más simple: solo use set -u

set -u se asegura de que cada variable referenciada se establezca cuando se usa, así que solo configúrela y olvídela

myFunction() {
    set -u
    if [ "$1" -gt "-1" ]; then
        echo hi
    fi

}
AndrewD
fuente