Sprawdź liczbę argumentów przekazanych do skryptu Bash

726

Chciałbym, aby mój skrypt Bash wydrukował komunikat o błędzie, jeśli wymagana liczba argumentów nie jest spełniona.

Próbowałem następującego kodu:

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

Z nieznanego powodu mam następujący błąd:

test: line 4: [2: command not found

Co ja robię źle?

Naftaly
źródło
54
Nie powinieneś nazywać swojego skryptu test. To jest nazwa standardowego polecenia Uniksa, nie chciałbyś go ukrywać.
Barmar
20
Zawsze używaj spacji wokół „[” („[[”) lub „(” („((')) w instrukcjach if w bash.
zoska
5
Aby dodać do komentarza @zoska, potrzebujesz spacji przed [ponieważ jest on zaimplementowany jako polecenie, spróbuj „które [”.
Daniel Da Cunha
1
lepszy przykład znajduje się pod linkiem poniżej: stackoverflow.com/questions/4341630/…
ramkrishna
3
@Barmar na pewno nazywa to w testporządku, o ile nie ma go na ŚCIEŻCE?
user253751

Odpowiedzi:

1098

Podobnie jak każde inne proste polecenie [ ... ]lub testwymaga spacji między jego argumentami.

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

Lub

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

Propozycje

Będąc w Bash, wolisz używać [[ ]]zamiast tego, ponieważ nie powoduje dzielenia słów i rozwijania nazw ścieżek zamiast zmiennych, że cytowanie może nie być konieczne, chyba że jest częścią wyrażenia.

[[ $# -ne 1 ]]

Posiada również inne funkcje, takie jak niecytowane grupowanie warunków, dopasowanie wzorca (rozszerzone dopasowanie wzorca z extglob) i dopasowanie wyrażenia regularnego.

Poniższy przykład sprawdza, czy argumenty są poprawne. Pozwala na pojedynczy argument lub dwa.

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

Dla czystych wyrażeń arytmetycznych, przy użyciu (( ))niektórych może być jeszcze lepiej, ale nadal są możliwe [[ ]]z jego operatorów arytmetycznych podoba -eq, -ne, -lt, -le, -gt, lub -gepoprzez umieszczenie wyrażenia jako pojedynczy argument wyrażenie:

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

To powinno być pomocne, jeśli musisz połączyć to z innymi funkcjami [[ ]].

Wyjście ze skryptu

Logiczne jest również zamknięcie skryptu, gdy zostaną do niego przekazane nieprawidłowe parametry. Zostało to już zasugerowane w komentarzach przez ekangas, ale ktoś edytował tę odpowiedź, aby otrzymać ją -1jako wartość zwróconą, więc równie dobrze mogę to zrobić poprawnie.

-1chociaż zaakceptowany przez Bash jako argument, exitnie jest wyraźnie udokumentowany i nie może być stosowany jako powszechna sugestia. 64Jest również najbardziej formalny, ponieważ wartość jest zdefiniowana w sysexits.hz #define EX_USAGE 64 /* command line usage error */. Większość narzędzi takich jak lszwraca również 2nieprawidłowe argumenty. Zwykłem też wracać 2w swoich skryptach, ale ostatnio tak naprawdę mnie to nie obchodziło i po prostu używałem 1wszystkich błędów. Ale po prostu umieśćmy 2tutaj, ponieważ jest to najczęściej i prawdopodobnie nie jest specyficzne dla systemu operacyjnego.

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

Bibliografia

konsolebox
źródło
2
OP: Pamiętaj, że [to tylko kolejne polecenie, np which [. Spróbuj .
Leo
5
Komendy @Leo mogą być wbudowane i nie mogą być. W bash [jest wbudowany, a [[słowo kluczowe. W niektórych starszych powłokach [nie jest nawet wbudowany. Polecenia takie jak [naturalnie koegzystują jako polecenia zewnętrzne w większości systemów, ale polecenia wewnętrzne są traktowane priorytetowo przez powłokę, chyba że obejdzie się za pomocą commandlub exec. Sprawdź dokumentację powłoki, jak oceniają. Zwróć uwagę na ich różnicę i sposób, w jaki mogą zachowywać się inaczej w każdej powłoce.
konsolebox
77

Dobrym pomysłem może być użycie wyrażeń arytmetycznych, jeśli masz do czynienia z liczbami.

if (( $# != 1 )); then
    echo "Illegal number of parameters"
fi
Aleks-Daniel Jakimenko-A.
źródło
Dlaczego w tym przypadku może to być dobry pomysł? Biorąc pod uwagę wydajność, przenośność i inne kwestie, czy nie najlepiej jest stosować najprostszą i najbardziej powszechnie rozumianą składnię, tj. [ ... ]Kiedy to dobrze działa i nie są wymagane żadne wymyślne operacje?
Max
Rozszerzenia arytmetyczne @Max $(( ))nie są wymyślne i powinny być implementowane przez wszystkie powłoki POSIX. Jednak (( ))składnia (bez $) nie jest jej częścią. Jeśli z jakiegoś powodu jesteś ograniczony, z pewnością możesz użyć [ ]zamiast tego, ale pamiętaj, że nie powinieneś [[ ]]również używać . Mam nadzieję, że rozumiesz pułapki [ ]i powody, dla których te funkcje istnieją. Ale to było pytanie Basha, więc udzielamy odpowiedzi Bash ( „Z reguły [[jest używane w przypadku ciągów i plików. Jeśli chcesz porównać liczby, użyj wyrażenia arytmetycznego” ).
Aleks-Daniel Jakimenko-A.
39

W []:! =, =, == ... są operatorami porównywania ciągów , a -eq, -gt ... są arytmetycznymi operatorami binarnymi.

Użyłbym:

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

Lub:

if [ $# -eq 1 ]; then
jhvaras
źródło
9
==jest właściwie nieudokumentowaną funkcją, która zdarza się współpracować z GNU test. Zdarza się również , że działa z FreeBSD test, ale może nie działać na Foo test . Tylko standardowe porównanie jest =(tylko FYI).
Martin Tournoij,
1
Jest to udokumentowane przy wejściu do bash man: Gdy używane są operatory == i! =, Ciąg po prawej stronie operatora jest uważany za wzorzec i dopasowywany zgodnie z zasadami opisanymi poniżej w części Dopasowywanie wzorców. Jeśli włączona jest opcja powłoki nocasematch, dopasowanie jest wykonywane bez względu na wielkość liter. Zwracana wartość to 0, jeśli łańcuch pasuje (==) lub nie pasuje (! =) Do wzorca, a 1 w przeciwnym razie. Każda część wzorca może być cytowana, aby wymusić dopasowanie go jako łańcucha.
jhvaras
2
@jhvaras: Dokładnie tak powiedział Carpetsmoker: może działać w niektórych implementacjach (i rzeczywiście działa w Bash), ale nie jest zgodny z POSIX . Na przykład, to nie z dash: dash -c '[ 1 == 1 ]'. POSIX określa tylko =, a nie ==.
gniourf_gniourf
34

Jeśli interesuje Cię zwolnienie za kaucją tylko w przypadku braku konkretnego argumentu, podstawienie parametru jest świetne:

#!/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
Poklepać
źródło
czy to nie jest pełne bashism?
Dwight Spencer
@DwightSpencer Czy to ma znaczenie?
konsolebox
@Temak Mogę, jeśli masz konkretne pytania, ale link do artykułu wyjaśnia to lepiej niż ja.
Pat
13

Prosty liniowiec, który działa, można wykonać za pomocą:

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

To rozkłada się na:

  1. przetestuj zmienną bash pod kątem wielkości parametrów $ # nie równa się 1 (nasza liczba poleceń podrzędnych)
  2. jeśli true, to wywołaj funkcję use () i zakończ ze statusem 1
  3. w przeciwnym razie wywołaj funkcję main ()

Uważa, że ​​należy pamiętać:

  • use () może być prostym echem „0 $: params”
  • main może być jednym długim skryptem
Dwight Spencer
źródło
1
Jeśli masz inny zestaw linii po tej linii, byłoby to błędne, ponieważ exit 1dotyczyłoby tylko kontekstu podpowłoki, czyniąc go po prostu synonimem ( usage; false ). Nie jestem fanem tego rodzaju uproszczenia, jeśli chodzi o analizę opcji, ale możesz użyć { usage && exit 1; }zamiast tego. A może po prostu { usage; exit 1; }.
konsolebox
1
@konsolebox (wykorzystanie i wyjście 1) działa dla ksh, zsh i bash wracając do bash 2.0. Składnia {...} jest najnowsza od 4.0+ bash. Nie zrozum mnie źle, jeśli jeden sposób działa dla ciebie dobrze, a następnie go wykorzystaj, ale pamiętaj, że nie wszyscy używają tej samej implementacji bash, którą robisz, i że powinniśmy kodować standardy posix, a nie bashisms.
Dwight Spencer
Nie jestem pewien, co mówisz. {...}jest powszechną składnią i jest dostępna dla większości, jeśli nie wszystkich powłok opartych na sh, nawet tych starszych powłok, które nie spełniają standardów POSIX.
konsolebox
7

Sprawdź ten cheashheash bash, może pomóc wiele.

Aby sprawdzić długość przekazywanych argumentów, użyj "$#"

Aby użyć tablicy przekazanych argumentów, użyj "$@"

Przykładem sprawdzania długości i iteracji byłoby:

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

myFunc "$@"

Ten artykuł pomógł mi, ale brakowało mi kilku rzeczy dla mnie i mojej sytuacji. Mam nadzieję, że to komuś pomaga.

Nick Hall
źródło
0

Jeśli chcesz być po bezpiecznej stronie, polecam użyć getopts.

Oto mały przykład:

    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

zobacz więcej szczegółów tutaj, na przykład http://wiki.bash-hackers.org/howto/getopts_tutorial

Izaak
źródło
0

Tutaj prosty jeden linijka, aby sprawdzić, czy podano tylko jeden parametr, w przeciwnym razie wyjdź ze skryptu:

[ "$#" -ne 1 ] && echo "USAGE $0 <PARAMETER>" && exit
panticz
źródło
-1

Powinieneś dodać spacje między warunkami testu:

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

Mam nadzieję, że to pomoże.

Fabricio
źródło