Verifique el número de argumentos pasados ​​a un script Bash

727

Me gustaría que mi script Bash imprima un mensaje de error si no se cumple el recuento de argumentos requerido.

Intenté el siguiente código:

#!/bin/bash
echo Script name: $0
echo $# arguments 
if [$# -ne 1]; 
    then echo "illegal number of parameters"
fi

Por alguna razón desconocida, tengo el siguiente error:

test: line 4: [2: command not found

¿Qué estoy haciendo mal?

Naftaly
fuente
54
No deberías nombrar tu guión test. Ese es el nombre de un comando estándar de Unix, no querrás seguirlo.
Barmar
20
Siempre use espacios alrededor de '[' ('[[') o '(' ('((') en declaraciones if en bash.
zoska
55
Para agregar al comentario de @zoska, necesita un espacio antes [porque está implementado como un comando, intente 'which ['.
Daniel Da Cunha
1
mejor ejemplo se da en el siguiente enlace: stackoverflow.com/questions/4341630/…
ramkrishna
3
@Barmar seguramente nombrarlo testestá bien, siempre y cuando no esté en la RUTA?
user253751

Respuestas:

1099

Al igual que cualquier otro comando simple, [ ... ]o testrequiere espacios entre sus argumentos.

if [ "$#" -ne 1 ]; then
    echo "Illegal number of parameters"
fi

O

if test "$#" -ne 1; then
    echo "Illegal number of parameters"
fi

Sugerencias

Cuando [[ ]]esté en Bash, prefiera usar en su lugar, ya que no divide las palabras ni expande el nombre de ruta a sus variables, por lo que es posible que no sea necesario citar, a menos que sea parte de una expresión.

[[ $# -ne 1 ]]

También tiene algunas otras características como agrupación de condiciones sin comillas, coincidencia de patrones (coincidencia de patrones extendida con extglob) y coincidencia de expresiones regulares.

El siguiente ejemplo verifica si los argumentos son válidos. Permite un solo argumento o dos.

[[ ($# -eq 1 || ($# -eq 2 && $2 == <glob pattern>)) && $1 =~ <regex pattern> ]]

Para expresiones aritméticas puras, utilizando (( ))a algunos todavía puede ser mejor, pero todavía son posibles en [[ ]]con sus operadores aritméticos gustaría -eq, -ne, -lt, -le, -gt, o -gemediante la colocación de la expresión como un único argumento de cadena:

A=1
[[ 'A + 1' -eq 2 ]] && echo true  ## Prints true.

Eso debería ser útil si necesita combinarlo con otras características [[ ]]también.

Salir del guión

También es lógico hacer que la secuencia de comandos salga cuando se le pasan parámetros no válidos. Esto ya ha sido sugerido en los comentarios de ekangas, pero alguien editó esta respuesta para tenerla -1como el valor devuelto, por lo que bien podría hacerlo bien.

-1aunque Bash lo acepta como argumento, exitno está explícitamente documentado y no es correcto utilizarlo como una sugerencia común. 64también es el valor más formal ya que se define sysexits.hcon #define EX_USAGE 64 /* command line usage error */. La mayoría de las herramientas lstambién devuelven 2argumentos no válidos. También solía volver 2en mis scripts, pero últimamente ya no me importaba, y simplemente lo usaba 1en todos los errores. Pero ubicémonos 2aquí, ya que es más común y probablemente no específico del sistema operativo.

if [[ $# -ne 1 ]]; then
    echo "Illegal number of parameters"
    exit 2
fi

Referencias

konsolebox
fuente
2
OP: Tenga en cuenta que [es solo otro comando, es decir, intente which [.
Leo
55
Los comandos @Leo pueden ser incorporados, y no pueden serlo. En bash, [es un incorporado, mientras que [[es una palabra clave. En algunas conchas más viejas, [ni siquiera está incorporado. Los comandos como, [naturalmente, coexisten como un comando externo en la mayoría de los sistemas, pero el shell prioriza los comandos internos a menos que omita con commando exec. Consulte la documentación del shell sobre cómo evalúan. Tome nota de su diferencia y de cómo pueden comportarse de manera diferente en cada caparazón.
konsolebox
78

Puede ser una buena idea usar expresiones aritméticas si se trata de números.

if (( $# != 1 )); then
    echo "Illegal number of parameters"
fi
Aleks-Daniel Jakimenko-A.
fuente
¿Por qué podría ser una buena idea, en el presente caso? Teniendo en cuenta la eficiencia, la portabilidad y otros problemas, ¿no es mejor usar la sintaxis más simple y más universalmente entendida, es decir,[ ... ] , cuando esto funciona bien y no se necesitan operaciones sofisticadas?
Max
Las expansiones aritméticas @Max $(( ))no son sofisticadas y deben implementarse en todos los shells POSIX. Sin embargo, la (( ))sintaxis (sin $) no forma parte de ella. Si por alguna razón está limitado, entonces seguramente puede usarlo [ ]en su lugar, pero tenga en cuenta que no debería usarlo [[ ]]también. Espero que comprenda las trampas [ ]y las razones por las que existen estas características. Pero esta era una pregunta de Bash, por lo que estamos dando respuestas de Bash ( "Como regla general, [[se usa para cadenas y archivos. Si desea comparar números, use una expresión aritmética" ).
Aleks-Daniel Jakimenko-A.
40

En []:! =, =, == ... son :! operadores de comparación de cadenas y -eq, -gt ... son aritméticos binarios.

Yo usaría:

if [ "$#" != "1" ]; then

O:

if [ $# -eq 1 ]; then
jhvaras
fuente
99
==En realidad, es una característica no documentada, que pasa a trabajar con GNU test. También sucede que el trabajo con FreeBSD test, pero puede que no funcione en foo test . La única comparación estándar es =(solo para su información).
Martin Tournoij
1
Está documentado en la entrada de bash man: cuando se usan los operadores == y! =, La cadena a la derecha del operador se considera un patrón y coincide de acuerdo con las reglas que se describen a continuación en Coincidencia de patrones. Si la opción de shell nocasematch está habilitada, la coincidencia se realiza sin tener en cuenta el caso de los caracteres alfabéticos. El valor de retorno es 0 si la cadena coincide (==) o no coincide (! =) Con el patrón, y 1 en caso contrario. Se puede citar cualquier parte del patrón para forzarlo a que coincida como una cadena.
jhvaras
2
@jhvaras: Eso es exactamente lo que dijo Carpetsmoker: puede funcionar en algunas implementaciones (y de hecho, funciona en Bash), pero no es compatible con POSIX . Por ejemplo, se fallará con dash: dash -c '[ 1 == 1 ]'. POSIX solo especifica =, y no ==.
gniourf_gniourf
34

Si solo está interesado en rescatar si falta un argumento en particular, la sustitución de parámetros es excelente:

#!/bin/bash
# usage-message.sh

: ${1?"Usage: $0 ARGUMENT"}
#  Script exits here if command-line parameter absent,
#+ with following error message.
#    usage-message.sh: 1: Usage: usage-message.sh ARGUMENT
Palmadita
fuente
¿No está eso cargado de bashisms?
Dwight Spencer
@DwightSpencer ¿Importaría?
konsolebox
@Temak Puedo si tiene preguntas específicas, pero el artículo vinculado lo explica mejor que yo.
Pat
13

Un revestimiento simple que funciona puede hacerse usando:

[ "$#" -ne 1 ] && ( usage && exit 1 ) || main

Esto se descompone en:

  1. prueba la variable bash para el tamaño de los parámetros $ # no es igual a 1 (nuestro número de subcomandos)
  2. si es verdadero, llame a la función use () y salga con el estado 1
  3. de lo contrario, llame a la función main ()

Piensa tener en cuenta:

  • use () puede ser simple echo "$ 0: params"
  • main puede ser un guión largo
Dwight Spencer
fuente
1
Si tiene otro conjunto de líneas después de esa línea, eso sería incorrecto, ya exit 1que solo se aplicaría al contexto de la subshell, por lo que también es así ( usage; false ). No soy un fanático de esa forma de simplificación cuando se trata del análisis de opciones, pero puedes usarlo { usage && exit 1; }en su lugar. O probablemente solo { usage; exit 1; }.
konsolebox
1
@konsolebox (uso && salida 1) funciona para ksh, zsh y bash volviendo a bash 2.0. La sintaxis {...} solo es reciente para 4.0+ de bash. No me malinterpreten si una forma funciona bien para usted, luego úsela, pero recuerde que no todos usan la misma implementación de bash que usted y debemos codificar para estándares posix, no bashisms.
Dwight Spencer
No estoy seguro de lo que estás diciendo. {...}es una sintaxis común y está disponible para la mayoría de los shells, si no todos sh, incluso aquellos que no cumplen con los estándares POSIX.
konsolebox
7

Echa un vistazo a esta hoja de trucos de bash, puede ayudar mucho.

Para verificar la longitud de los argumentos pasados, utiliza "$#"

Para usar la matriz de argumentos pasados, usa "$@"

Un ejemplo de verificar la longitud e iterar sería:

myFunc() {
  if [[ "$#" -gt 0 ]]; then
    for arg in "$@"; do
      echo $arg
    done
  fi
}

myFunc "$@"

Esto articulado me ayudó, pero me faltaban algunas cosas para mí y mi situación. Esperemos que esto ayude a alguien.

Nick Hall
fuente
0

En caso de que quiera estar seguro, le recomiendo usar getopts.

Aquí hay un pequeño ejemplo:

    while getopts "x:c" opt; do
      case $opt in
        c)
          echo "-$opt was triggered, deploy to ci account" >&2
          DEPLOY_CI_ACCT="true"
          ;;
            x)
              echo "-$opt was triggered, Parameter: $OPTARG" >&2 
              CMD_TO_EXEC=${OPTARG}
              ;;
            \?)
              echo "Invalid option: -$OPTARG" >&2 
              Usage
              exit 1
              ;;
            :)
              echo "Option -$OPTARG requires an argument." >&2 
              Usage
              exit 1
              ;;
          esac
        done

ver más detalles aquí, por ejemplo http://wiki.bash-hackers.org/howto/getopts_tutorial

IsaacE
fuente
0

Aquí, un simple revestimiento para verificar si solo se proporciona un parámetro; de lo contrario, salga del script:

[ "$#" -ne 1 ] && echo "USAGE $0 <PARAMETER>" && exit
panticz
fuente
-1

Debe agregar espacios entre la condición de prueba:

if [ $# -ne 1 ]; 
    then echo "illegal number of parameters"
fi

Espero que esto ayude.

Fabricio
fuente