Überprüfen Sie die Anzahl der an ein Bash-Skript übergebenen Argumente

727

Ich möchte, dass mein Bash-Skript eine Fehlermeldung ausgibt, wenn die erforderliche Anzahl von Argumenten nicht erreicht wird.

Ich habe den folgenden Code ausprobiert:

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

Aus einem unbekannten Grund habe ich folgenden Fehler erhalten:

test: line 4: [2: command not found

Was mache ich falsch?

Naftaly
quelle
54
Sie sollten Ihr Skript nicht benennen test. Das ist der Name eines Standard-Unix-Befehls, den Sie nicht beschatten möchten.
Barmar
20
Verwenden Sie in if-Anweisungen in bash immer Leerzeichen um '[' ('[[') oder '(' ('(('))).
zoska
5
Um einen @ zoska-Kommentar hinzuzufügen, benötigen Sie ein Leerzeichen vor [da es als Befehl implementiert ist, versuchen Sie 'which ['.
Daniel Da Cunha
1
Ein besseres Beispiel finden Sie unter dem folgenden Link: stackoverflow.com/questions/4341630/…
ramkrishna
3
@Barmar es sicher zu benennen testist in Ordnung, solange es nicht auf dem Pfad ist?
user253751

Antworten:

1099

Genau wie jeder andere einfache Befehl [ ... ]oder testerfordert Leerzeichen zwischen seinen Argumenten.

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

Oder

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

Vorschläge

Wenn Sie in Bash sind, bevorzugen Sie [[ ]]stattdessen die Verwendung, da keine Wortaufteilung und Pfadnamenerweiterung für die Variablen durchgeführt wird. Ein Anführungszeichen ist möglicherweise nur erforderlich, wenn es Teil eines Ausdrucks ist.

[[ $# -ne 1 ]]

Es hat auch einige andere Funktionen wie nicht zitierte Bedingungsgruppierung, Musterabgleich (erweiterter Musterabgleich mit extglob) und Regex-Abgleich.

Im folgenden Beispiel wird überprüft, ob Argumente gültig sind. Es erlaubt ein oder zwei Argumente.

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

Für reine arithmetische Ausdrücke verwenden (( ))kann noch besser zu einigen, aber sie sind immer noch möglich , in [[ ]]mit arithmetischen Operatoren wie -eq, -ne, -lt, -le, -gt, oder -gedurch den Ausdruck als ein einzelnes Zeichenfolge Argument setzen:

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

Dies sollte hilfreich sein, wenn Sie es auch mit anderen Funktionen von kombinieren müssen [[ ]].

Beenden des Skripts

Es ist auch logisch, das Skript zu beenden, wenn ungültige Parameter an das Skript übergeben werden. Dies wurde bereits in den Kommentaren von ekangas vorgeschlagen, aber jemand hat diese Antwort bearbeitet, um sie -1als Rückgabewert zu verwenden, also könnte ich es genauso gut richtig machen.

-1Obwohl von Bash als Argument akzeptiert, exitist dies nicht explizit dokumentiert und kann nicht als allgemeiner Vorschlag verwendet werden. 64ist auch der formalste Wert, da er in sysexits.hmit definiert ist #define EX_USAGE 64 /* command line usage error */. Die meisten Tools geben lsauch 2ungültige Argumente zurück. Ich habe auch 2in meinen Skripten zurückgegeben, aber in letzter Zeit habe ich mich nicht mehr wirklich darum gekümmert und einfach bei 1allen Fehlern verwendet. Aber lassen Sie uns einfach 2hier platzieren, da es am häufigsten und wahrscheinlich nicht betriebssystemspezifisch ist.

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

Verweise

konsolebox
quelle
2
OP: Denken Sie daran, dass dies [nur ein weiterer Befehl ist, dh versuchen Sie es which [.
Leo
5
@ Leo-Befehle können eingebaut werden und nicht. In bash [ist ein eingebautes, während [[ein Schlüsselwort ist. In einigen älteren Muscheln [ist nicht einmal eingebaut. Befehle wie [natürlich existieren in den meisten Systemen als externer Befehl nebeneinander, aber interne Befehle werden von der Shell priorisiert, sofern Sie nicht mit commandoder umgehen exec. Überprüfen Sie in der Dokumentation der Shell, wie sie bewertet wird. Beachten Sie ihren Unterschied und wie sie sich in jeder Schale unterschiedlich verhalten können.
konsolebox
78

Es könnte eine gute Idee sein, arithmetische Ausdrücke zu verwenden, wenn Sie mit Zahlen arbeiten.

if (( $# != 1 )); then
    echo "Illegal number of parameters"
fi
Aleks-Daniel Jakimenko-A.
quelle
Warum könnte das im vorliegenden Fall eine gute Idee sein? Ist es in Anbetracht der Effizienz, Portabilität und anderer Probleme nicht am besten, die einfachste und allgemeinste Syntax zu verwenden, dh [ ... ]wenn dies einwandfrei funktioniert und keine ausgefallenen Operationen erforderlich sind?
Max
@ Max arithmetische Erweiterungen $(( ))sind nichts Besonderes und sollten von allen POSIX-Shells implementiert werden. Die (( ))Syntax (ohne $) ist jedoch nicht Teil davon. Wenn Sie aus irgendeinem Grund eingeschränkt sind, können Sie sicherlich [ ]stattdessen verwenden, aber denken Sie daran, dass Sie dann auch nicht verwenden sollten [[ ]]. Ich hoffe, Sie verstehen die Fallstricke [ ]und Gründe, warum diese Funktionen existieren. Da dies jedoch eine Bash-Frage war, geben wir Bash-Antworten ( „Als Faustregel wird [[für Zeichenfolgen und Dateien verwendet. Wenn Sie Zahlen vergleichen möchten, verwenden Sie einen ArithmeticExpression“ ).
Aleks-Daniel Jakimenko-A.
40

On [] :! =, =, == ... sind String- Vergleichsoperatoren und -eq, -gt ... sind arithmetische binäre.

Ich würde ... benutzen:

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

Oder:

if [ $# -eq 1 ]; then
jhvaras
quelle
9
==ist eigentlich eine undokumentierte Funktion, die zufällig mit GNU funktioniert test. Es ist auch geschieht mit FreeBSD zu arbeiten test, sondern kann auf nicht funktionieren foo test . Der einzige Standardvergleich ist =(nur zu Ihrer Information).
Martin Tournoij
1
Es wird beim Bash-Man-Eintrag dokumentiert: Wenn die Operatoren == und! = Verwendet werden, wird die Zeichenfolge rechts vom Operator als Muster betrachtet und gemäß den unten unter Musterabgleich beschriebenen Regeln abgeglichen. Wenn die Shell-Option nocasematch aktiviert ist, wird die Übereinstimmung ohne Berücksichtigung von alphabetischen Zeichen durchgeführt. Der Rückgabewert ist 0, wenn die Zeichenfolge mit dem Muster übereinstimmt (==) oder nicht übereinstimmt (! =), Andernfalls 1. Jeder Teil des Musters kann in Anführungszeichen gesetzt werden, um zu erzwingen, dass es als Zeichenfolge abgeglichen wird.
Jhvaras
2
@jhvaras: Genau das hat Carpetsmoker gesagt: Es funktioniert möglicherweise in einigen Implementierungen (und zwar in Bash), ist aber nicht POSIX-kompatibel . Zum Beispiel schlägt es fehl mit dash: dash -c '[ 1 == 1 ]'. POSIX gibt nur an =und nicht ==.
gniourf_gniourf
34

Wenn Sie nur dann gegen Kaution gehen möchten, wenn ein bestimmtes Argument fehlt, ist die Parametersubstitution großartig:

#!/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
Klopfen
quelle
Ist das nicht voller Bashismen?
Dwight Spencer
@ DwightSpencer Würde es etwas ausmachen?
konsolebox
@Temak Ich kann, wenn Sie spezielle Fragen haben, aber der verlinkte Artikel erklärt es besser als ich.
Pat
13

Ein einfacher Einzeiler, der funktioniert, kann verwendet werden mit:

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

Dies gliedert sich in:

  1. Testen Sie die Bash-Variable auf die Größe der Parameter. $ # ist nicht gleich 1 (unsere Anzahl der Unterbefehle).
  2. Wenn true, rufen Sie die Funktion usage () auf und beenden Sie sie mit Status 1
  3. Andernfalls rufen Sie die Funktion main () auf

Denkt zu beachten:

  • usage () kann einfach ein Echo sein "$ 0: params"
  • main kann ein langes Skript sein
Dwight Spencer
quelle
1
Wenn Sie nach dieser Zeile einen anderen Satz von Zeilen haben, wäre dies falsch, da exit 1dies nur für den Kontext der Unterschale gelten würde, was es auch nur bedeutet ( usage; false ). Ich bin kein Fan dieser Art der Vereinfachung, wenn es um das Parsen von Optionen geht, aber Sie können { usage && exit 1; }stattdessen verwenden. Oder wahrscheinlich nur { usage; exit 1; }.
konsolebox
1
@konsolebox (Verwendung && Exit 1) funktioniert für ksh, zsh und bash und geht zurück zu bash 2.0. Die {...} Syntax ist nur bis 4.0+ von bash aktuell. Verstehen Sie mich nicht falsch, wenn ein Weg für Sie gut funktioniert, dann verwenden Sie ihn, aber denken Sie daran, dass nicht jeder die gleiche Implementierung von Bash verwendet wie Sie, und wir sollten codieren, um Standards zu setzen, nicht Bashismen.
Dwight Spencer
Ich bin mir nicht sicher, was du sagst. {...}ist eine gängige Syntax und steht den meisten, wenn nicht allen Shells zur Verfügung, die darauf basieren sh, auch den älteren Shells, die nicht den POSIX-Standards entsprechen.
konsolebox
7

Überprüfen Sie dies Bash-Cheatsheet an, es kann viel helfen.

Um die Länge der übergebenen Argumente zu überprüfen, verwenden Sie "$#"

Um das übergebene Array von Argumenten zu verwenden, verwenden Sie "$@"

Ein Beispiel für das Überprüfen der Länge und das Iterieren wäre:

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

myFunc "$@"

Dieser Artikel half mir, aber es fehlten ein paar Dinge für mich und meine Situation. Hoffentlich hilft das jemandem.

Nick Hall
quelle
0

Wenn Sie auf der sicheren Seite sein möchten, empfehle ich die Verwendung von getopts.

Hier ist ein kleines Beispiel:

    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

Weitere Details finden Sie hier, zum Beispiel http://wiki.bash-hackers.org/howto/getopts_tutorial

IsaacE
quelle
0

Hier ein einfacher Einzeiler, um zu überprüfen, ob nur ein Parameter angegeben ist. Andernfalls beenden Sie das Skript:

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

Sie sollten Leerzeichen zwischen den Testbedingungen einfügen:

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

Ich hoffe das hilft.

Fabricio
quelle