Controlla il numero di argomenti passati a uno script Bash


727

Vorrei che il mio script Bash stampasse un messaggio di errore se il conteggio degli argomenti richiesto non veniva soddisfatto.

Ho provato il seguente codice:

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

Per qualche ragione sconosciuta ho il seguente errore:

test: line 4: [2: command not found

Che cosa sto facendo di sbagliato?


54
Non dovresti dare un nome alla tua sceneggiatura test. Questo è il nome di un comando Unix standard, non vorrai metterlo in ombra.
Barmar,

20
Usa sempre spazi attorno a '[' ('[[') o '(' ('((') in if istruzioni in bash.
zoska

5
Per aggiungere un commento a @zoska, è necessario uno spazio prima di [poiché è implementato come comando, provare 'che ['.
Daniel Da Cunha,

1
esempio migliore è dato sul link sottostante: stackoverflow.com/questions/4341630/...
Ramkrishna

3
@Barmar sicuramente nominarlo testva bene purché non sia sul PERCORSO?
user253751

Risposte:


1099

Proprio come qualsiasi altro comando semplice [ ... ]o testrichiede spazi tra i suoi argomenti.

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

O

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

suggerimenti

Quando sei in Bash, preferisci usare [[ ]]invece come non fa la suddivisione delle parole e l'espansione del percorso alle sue variabili che la citazione potrebbe non essere necessaria a meno che non faccia parte di un'espressione.

[[ $# -ne 1 ]]

Ha anche alcune altre caratteristiche come il raggruppamento delle condizioni non quotate, la corrispondenza dei modelli (corrispondenza dei modelli estesa con extglob) e la corrispondenza regex.

L'esempio seguente verifica se gli argomenti sono validi. Permette un singolo argomento o due.

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

Per espressioni aritmetiche puri, utilizzando (( ))ad alcuni può essere ancora meglio, ma sono ancora possibili in [[ ]]con i suoi operatori aritmetici desiderate -eq, -ne, -lt, -le, -gt, oppure -geposizionando l'espressione come un singolo argomento stringa:

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

Ciò dovrebbe essere utile se fosse necessario combinarlo con altre funzionalità di [[ ]].

Uscita dalla sceneggiatura

È inoltre logico uscire dallo script quando gli vengono passati parametri non validi. Questo è già stato suggerito nei commenti da ekangas ma qualcuno ha modificato questa risposta per averla -1come valore restituito, quindi potrei anche farlo bene.

-1sebbene accettato da Bash come argomento, exitnon è esplicitamente documentato e non è giusto usarlo come suggerimento comune. 64è anche il valore più formale poiché è definito sysexits.hcon #define EX_USAGE 64 /* command line usage error */. Molti strumenti come lsrestituiscono anche 2argomenti non validi. Ero anche abituato a tornare 2nei miei script, ma ultimamente non mi importava più molto, e semplicemente usavo 1tutti gli errori. Ma posizioniamoci 2qui poiché è più comune e probabilmente non specifico del sistema operativo.

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

Riferimenti


2
OP: Tieni presente che [è solo un altro comando, ad esempio, prova which [.
Leone

5
I comandi @Leo possono essere integrati e non possono esserlo. In bash, [è incorporato, mentre [[è una parola chiave. In alcune vecchie conchiglie, [non è nemmeno incorporato. I comandi come [naturalmente coesistono come comandi esterni nella maggior parte dei sistemi, ma i comandi interni sono prioritari dalla shell a meno che non si ignori con commando exec. Controlla la documentazione della shell su come valutano. Prendi nota della loro differenza e di come possono comportarsi diversamente in ogni shell.
konsolebox

78

Potrebbe essere una buona idea usare espressioni aritmetiche se hai a che fare con i numeri.

if (( $# != 1 )); then
    echo "Illegal number of parameters"
fi

Perché potrebbe essere una buona idea, nel caso di specie? Considerando l'efficienza, la portabilità e altri problemi, non è meglio usare la sintassi più semplice e universalmente capita, cioè [ ... ]quando questo funziona bene e non sono necessarie operazioni fantasiose?
Max

Le espansioni aritmetiche di @Max $(( ))non sono fantasiose e dovrebbero essere implementate da tutte le shell POSIX. Tuttavia, la (( ))sintassi (senza $) non fa parte di essa. Se per qualche motivo sei limitato, sicuramente puoi usare [ ]invece, ma tieni presente che non dovresti usare [[ ]]anche. Spero che tu capisca le insidie [ ]e le ragioni per cui esistono queste caratteristiche. Ma questa era una domanda di Bash, quindi stiamo dando a Bash risposte ( "Come regola empirica, [[è usato per stringhe e file. Se vuoi confrontare numeri, usa una espressione aritmetica" ).
Aleks-Daniel Jakimenko-A.

40

Su []:! =, =, == ... sono operatori di confronto di stringhe e -eq, -gt ... sono binari aritmetici .

Io userei:

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

O:

if [ $# -eq 1 ]; then

9
==è in realtà una caratteristica non documentata, che avviene a lavorare con GNU test. Inoltre capita di lavorare con FreeBSD test, ma potrebbe non funzionare su foo test . L' unico confronto standard è =(solo FYI).
Martin Tournoij,

1
È documentato sulla voce man bash: Quando vengono utilizzati gli operatori == e! =, La stringa a destra dell'operatore viene considerata un motivo e abbinata secondo le regole descritte di seguito in Corrispondenza dei motivi. Se l'opzione shell nocasematch è abilitata, la corrispondenza viene eseguita indipendentemente dal caso dei caratteri alfabetici. Il valore restituito è 0 se la stringa corrisponde (==) o non corrisponde (! =) Al modello e 1 in caso contrario. Qualsiasi parte del modello può essere quotata per forzare la corrispondenza con una stringa.
Jhvaras,

2
@jhvaras: Questo è esattamente ciò che ha detto Carpetsmoker: potrebbe funzionare in alcune implementazioni (e in effetti, funziona in Bash), ma non è conforme a POSIX . Ad esempio, sarà riuscire con dash: dash -c '[ 1 == 1 ]'. POSIX specifica solo =e non ==.
gniourf_gniourf

34

Se sei interessato al salvataggio solo se manca un argomento particolare, la sostituzione dei parametri è ottima:

#!/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

non è carico di basismi?
Dwight Spencer,

@DwightSpencer Avrebbe importanza?
konsolebox,

@Temak Posso se hai domande specifiche, ma l'articolo collegato spiega meglio di me.
Pat

13

Un semplice liner che funziona può essere fatto usando:

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

Questo si scompone in:

  1. prova la variabile bash per la dimensione dei parametri $ # non è uguale a 1 (il nostro numero di comandi secondari)
  2. se vero, chiama la funzione use () e termina con lo stato 1
  3. else chiama la funzione main ()

Pensa a notare:

  • use () può essere semplicemente un eco "$ 0: params"
  • main può essere una lunga sceneggiatura

1
Se si dispone di un altro set di righe dopo quella riga, ciò sarebbe errato poiché exit 1si applicherebbe solo al contesto della subshell rendendolo anche sinonimo di ( usage; false ). Non sono un fan di quel modo di semplificazione quando si tratta di analisi delle opzioni, ma puoi usare { usage && exit 1; }invece. O probabilmente solo { usage; exit 1; }.
konsolebox,

1
@konsolebox (utilizzo && uscita 1) funziona con ksh, zsh e bash che risale alla bash 2.0. La sintassi {...} è recente fino a 4.0+ di bash. Non fraintendetemi se un modo funziona bene per voi, quindi usatelo, ma ricordate che non tutti usano la stessa implementazione di bash che fate e dovremmo codificare per posix standard non bashismi.
Dwight Spencer,

Non sono sicuro di quello che stai dicendo. {...}è una sintassi comune ed è disponibile per la maggior parte se non per tutte le shell basate sh, anche per quelle più vecchie che non seguono gli standard POSIX.
konsolebox,

7

Dai un'occhiata a questo cheatsheet di bash, può aiutare molto.

Per verificare la lunghezza degli argomenti passati, si utilizza "$#"

Per usare l'array di argomenti passati, si usa "$@"

Un esempio di controllo della lunghezza e iterazione sarebbe:

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

myFunc "$@"

Ciò mi ha aiutato, ma mancavano alcune cose per me e la mia situazione. Spero che questo aiuti qualcuno.


0

Se vuoi essere al sicuro, ti consiglio di usare getopts.

Ecco un piccolo esempio:

    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

vedi maggiori dettagli qui per esempio http://wiki.bash-hackers.org/howto/getopts_tutorial


0

Qui una semplice fodera per verificare se viene fornito un solo parametro, altrimenti uscire dallo script:

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

-1

È necessario aggiungere spazi tra le condizioni di test:

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

Spero che questo possa essere d'aiuto.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.