Übergabe benannter Argumente an Shell-Skripte

114

Gibt es eine einfache Möglichkeit, benannte Parameter an ein Shell-Skript zu übergeben (zu empfangen)?

Zum Beispiel,

my_script -p_out '/some/path' -arg_1 '5'

Und im Inneren my_script.sherhalten sie als:

# I believe this notation does not work, but is there anything close to it?
p_out=$ARGUMENTS['p_out']
arg1=$ARGUMENTS['arg_1']

printf "The Argument p_out is %s" "$p_out"
printf "The Argument arg_1 is %s" "$arg1"

Ist das in Bash oder Zsh möglich?

Amelio Vazquez-Reina
quelle
2
haben einen Blick auf docopt - es hilft mit benannten Parametern und nicht Validierung Eingang auch
Schlagen

Antworten:

34

Die wahrscheinlich nächstliegende Syntax lautet:

p_out='/some/path' arg_1='5' my_script
Hauke ​​Laging
quelle
7
Wenn die -kOption in der aufrufenden Shell festgelegt ist, my_script p_out='/some/path' arg_1='5'hat dies den gleichen Effekt. (Alle Argumente in Form einer Zuweisung werden der Umgebung hinzugefügt, nicht nur die Zuweisungen vor dem Befehl.)
Chepner
13
Früher habe ich diese Syntax geliebt, aber sie hat eine GROSSE Einschränkung: Nach der Ausführung von Befehlen / Funktionen werden diese Variablen immer noch im aktuellen Gültigkeitsbereich definiert! ZB: x=42 echo $x; echo $xWas bedeutet, dass bei der nächsten Ausführung von my_script, wenn p_outweggelassen, der beim letzten Mal übergebene Wert beibehalten wird !! ( '/some/path')
Lucas Cimon
@LucasCimon Kannst du sie nicht unsetnach der ersten Ausführung zurücksetzen, bevor die nächste Ausführung?
Nikos Alexandris
2
@LucasCimon Das ist nicht richtig. x=42 echo $xgibt nicht einmal etwas aus, wenn $xes vorher nicht definiert wurde.
Hauke ​​Laging
Du hast Recht @HaukeLaging, danke, dass du das korrigiert hast
Lucas Cimon
148

Wenn es Ihnen nichts ausmacht, sich auf Argumentnamen mit nur einem Buchstaben zu beschränken, können my_script -p '/some/path' -a5Sie in bash die eingebaute Funktion verwenden getopts, z

#!/bin/bash

while getopts ":a:p:" opt; do
  case $opt in
    a) arg_1="$OPTARG"
    ;;
    p) p_out="$OPTARG"
    ;;
    \?) echo "Invalid option -$OPTARG" >&2
    ;;
  esac
done

printf "Argument p_out is %s\n" "$p_out"
printf "Argument arg_1 is %s\n" "$arg_1"

Dann kannst du machen

$ ./my_script -p '/some/path' -a5
Argument p_out is /some/path
Argument arg_1 is 5

Es gibt ein hilfreiches kleines getopts-Tutorial, oder Sie können es help getoptsan der Shell-Eingabeaufforderung eingeben .

Stahlfahrer
quelle
25
Dies sollte die akzeptierte Antwort sein
Kaushik Ghose
3
Ich weiß, das ist ein bisschen alt, aber warum nur 1 Buchstabe für die Argumente?
Kevin
1
Ich habe dies implementiert (aber mit iund d). Wenn ich es mit my_script -i asd -d asdstarte, bekomme ich eine leere Zeichenkette für das dArgument. Wenn ich es laufen my_script -d asd -i asdlasse, erhalte ich leere Zeichenkette für beide Argumente.
Milkncookiez
3
@ Milkncookiez - Ich hatte ein ähnliches Problem - Ich habe nach dem letzten Argument kein ':' eingefügt (ein 'w' in meinem Fall). Nachdem ich das ':' hinzugefügt hatte, fing es an, wie erwartet zu funktionieren
Derek
37

Ich habe dies von drupal.org gestohlen , aber Sie könnten so etwas tun:

while [ $# -gt 0 ]; do
  case "$1" in
    --p_out=*)
      p_out="${1#*=}"
      ;;
    --arg_1=*)
      arg_1="${1#*=}"
      ;;
    *)
      printf "***************************\n"
      printf "* Error: Invalid argument.*\n"
      printf "***************************\n"
      exit 1
  esac
  shift
done

Der einzige Nachteil ist, dass Sie die Syntax verwenden müssen my_script --p_out=/some/path --arg_1=5.

cdmo
quelle
7
Die Einschränkung ist nicht erforderlich. :) Sie können die Bedingungen wie folgt haben:-c|--condition)
Milkncookiez
28

Ich benutze dieses Skript und arbeite wie ein Zauber:

for ARGUMENT in "$@"
do

    KEY=$(echo $ARGUMENT | cut -f1 -d=)
    VALUE=$(echo $ARGUMENT | cut -f2 -d=)   

    case "$KEY" in
            STEPS)              STEPS=${VALUE} ;;
            REPOSITORY_NAME)    REPOSITORY_NAME=${VALUE} ;;     
            *)   
    esac    


done

echo "STEPS = $STEPS"
echo "REPOSITORY_NAME = $REPOSITORY_NAME"

Verwendungszweck

bash my_scripts.sh  STEPS="ABC" REPOSITORY_NAME="stackexchange"

Konsolenergebnis:

STEPS = ABC
REPOSITORY_NAME = stackexchange

STEPS und REPOSITORY_NAME können jetzt im Skript verwendet werden.

Es spielt keine Rolle, in welcher Reihenfolge die Argumente stehen.

JRichardsz
quelle
4
Dies ist ordentlich und sollte die akzeptierte Antwort sein.
Miguelmorin
15

Mit würden zshSie verwenden zparseopts:

#! /bin/zsh -
zmodload zsh/zutil
zparseopts -A ARGUMENTS -p_out: -arg_1:

p_out=$ARGUMENTS[--p_out]
arg1=$ARGUMENTS[--arg_1]

printf 'Argument p_out is "%s"\n' "$p_out"
printf 'Argument arg_1 is "%s"\n' "$arg_1"

Aber Sie würden das Skript mit aufrufen myscript --p_out foo.

Beachten Sie, dass zparseoptses nicht möglich ist, lange Optionen oder die --p_out=fooSyntax von GNU abzukürzen getopt(3).

Stéphane Chazelas
quelle
Wissen Sie, warum die zparseopts nur einen Gedankenstrich für die Argumente verwenden, während es in der []2 Gedankenstriche sind? Macht keinen Sinn!
Timo
@ Timo, siehe info zsh zparseoptsfür Details
Stéphane Chazelas
9

Ich habe mir gerade dieses Skript ausgedacht

while [ $# -gt 0 ]; do

   if [[ $1 == *"--"* ]]; then
        v="${1/--/}"
        declare $v="$2"
   fi

  shift
done

übergeben Sie es wie my_script --p_out /some/path --arg_1 5und dann im Skript können Sie $arg_1und verwenden $p_out.

Shahzad Malik
quelle
Ich mag diese Lösung in KSH88 Ich musste v=``echo ${1} | awk '{print substr($1,3)}'`` typeset $v="$2"(Entfernen Sie ein Backtick pro Seite)
hol
-2

Wenn eine Funktion oder eine Anwendung mehr als null Argumente hat, hat sie immer ein letztes Argument.

Wenn Sie Optionsflag- und Wertepaare lesen möchten, wie in: $ ./t.sh -o output -i input -l last

Und Sie möchten eine variable Anzahl von Option / Wert-Paaren akzeptieren,

Und will keinen riesigen "wenn ... dann ... sonst ... fi" -Baum,

Nachdem Sie auf eine Argumentanzahl ungleich Null und gerade geprüft haben,

Schreiben Sie eine while-Schleife mit diesen vier eval-Anweisungen als Rumpf, gefolgt von einer case-Anweisung mit den beiden Werten, die bei jedem Durchlauf der Schleife ermittelt werden.

Der knifflige Teil des Skripts wird hier gezeigt:

#!/bin/sh    

# For each pair - this chunk is hard coded for the last pair.
eval TMP="'$'$#"
eval "PICK=$TMP"
eval TMP="'$'$(($#-1))"
eval "OPT=$TMP"

# process as required - usually a case statement on $OPT
echo "$OPT \n $PICK"

# Then decrement the indices (as in third eval statement) 

:<< EoF_test
$ ./t.sh -o output -i input -l last
-l 
last
$ ./t.sh -o output -l last
-l 
last
$ ./t.sh  -l last
-l 
last
EoF_test
knc1
quelle
-3
mitsos@redhat24$ my_script "a=1;b=mitsos;c=karamitsos"
#!/bin/sh
eval "$1"

Sie haben gerade Befehlszeilenparameter in den Gültigkeitsbereich des Skripts eingefügt !!

Thettalos
quelle
3
Dies funktioniert nicht mit der vom OP angegebenen Syntax. sie wollen-a 1 -b mitsos -c karamitsos
Michael Mrozek