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?


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


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

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

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

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

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.


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


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

-1

Powinieneś dodać spacje między warunkami testu:

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

Mam nadzieję, że to pomoże.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.