Przekazywanie nazwanych argumentów do skryptów powłoki

114

Czy jest jakiś prosty sposób na przekazanie (odbiór) nazwanych parametrów do skryptu powłoki?

Na przykład,

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

I w środku my_script.shotrzymuj je jako:

# 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"

Czy jest to możliwe w Bash lub Zsh?

Amelio Vazquez-Reina
źródło
2
spójrz na docopt - pomaga przy nazwanych parametrach i sprawdza poprawność danych wejściowych
Beat

Odpowiedzi:

34

Prawdopodobnie najbliższa składnia to:

p_out='/some/path' arg_1='5' my_script
Hauke ​​Laging
źródło
7
W związku z tym, jeśli -kopcja jest ustawiona w powłoce wywołującej , my_script p_out='/some/path' arg_1='5'ma ten sam efekt. (Wszystkie argumenty w postaci przypisania są dodawane do środowiska, a nie tylko przypisania poprzedzające polecenie.)
chepner
13
Uwielbiałem tę składnię, ale ma ona DUŻE zastrzeżenie: po wykonaniu polecenia / funkcji zmienne te nadal będą zdefiniowane w bieżącym zakresie! Np .: x=42 echo $x; echo $xCo oznacza, że ​​przy następnym wykonaniu my_script, jeśli p_outzostanie pominięty, będzie się trzymać wartości przekazanej ostatnim razem !! ( '/some/path')
Lucas Cimon
@LucasCimon Czy nie możesz unsetich po pierwszej realizacji, zresetować przed kolejną?
Nikos Alexandris,
2
@LucasCimon To nie jest poprawne. x=42 echo $xnawet nie wypisuje niczego, jeśli $xnie zostało wcześniej zdefiniowane.
Hauke ​​Laging
Masz rację @HaukeLaging, dzięki za naprawienie tego
Lucas Cimon
147

Jeśli nie masz nic przeciwko ograniczeniu się do jednoliterowych nazw argumentów, tzn. my_script -p '/some/path' -a5W bash możesz użyć wbudowanego getopts, np

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

To możesz zrobić

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

Jest przydatny samouczek dla małych użytkowników Getopts lub można pisać help getoptsw wierszu poleceń powłoki.

steeldriver
źródło
25
To powinna być zaakceptowana odpowiedź
Kaushik Ghose
3
Wiem, że to trochę stare, ale dlaczego tylko 1 litera na argumenty?
Kevin
1
Zaimplementowałem to (ale z ii d). Kiedy go uruchomię, my_script -i asd -d asdotrzymuję pusty ciąg dargumentu. Po uruchomieniu my_script -d asd -i asdotrzymuję pusty ciąg dla obu argumentów.
Milkncookiez
3
@Milkncookiez - Miałem podobny problem - nie wstawiłem „:” po ostatnim argumencie (w moim przypadku „w”). Kiedy dodałem „:”, zaczęło działać zgodnie z oczekiwaniami
Derek
37

Ukradłem to z drupal.org , ale możesz zrobić coś takiego:

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

Jedynym zastrzeżeniem jest to, że musisz użyć składni my_script --p_out=/some/path --arg_1=5.

cdmo
źródło
7
Zastrzeżenie nie jest konieczne. :) Możesz spełnić następujące warunki:-c|--condition)
Milkncookiez
28

Używam tego skryptu i działa jak urok:

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"

Stosowanie

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

Wynik konsoli:

STEPS = ABC
REPOSITORY_NAME = stackexchange

STEPS i REPOSITORY_NAME są gotowe do użycia w skrypcie.

Nie ma znaczenia, w jakiej kolejności są argumenty.

JRichardsz
źródło
4
To jest miłe i powinno być przyjętą odpowiedzią.
miguelmorin
15

Z zshużyłbyś 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"

Ale wywołałbyś skrypt za pomocą myscript --p_out foo.

Zauważ, że zparseoptsnie obsługuje skrótów długich opcji ani --p_out=fooskładni, jak GNU getopt(3).

Stéphane Chazelas
źródło
Czy wiesz, dlaczego zparseopts używa tylko jednego myślnika dla argumentów, podczas gdy w []nim są 2 myślniki? Nie ma sensu!
Timo,
@ Timo, zobacz info zsh zparseoptsszczegóły
Stéphane Chazelas,
9

Właśnie wymyśliłem ten skrypt

while [ $# -gt 0 ]; do

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

  shift
done

przekazać go jak, my_script --p_out /some/path --arg_1 5a następnie w skrypcie można użyć $arg_1i $p_out.

Shahzad Malik
źródło
Podoba mi się to rozwiązanie w KSH88, które musiałem v=``echo ${1} | awk '{print substr($1,3)}'`` typeset $v="$2"(usunąć jeden backtick z każdej strony)
hol
-2

Jeśli funkcja lub aplikacja ma więcej niż zero argumentów, zawsze ma ostatni argument.

Jeśli chcesz odczytać flagę opcji i pary wartości, jak w: $ ./t.sh -o output -i input -l last

I chcesz zaakceptować zmienną liczbę par opcji / wartości,

I nie chcę wielkiego drzewa „jeśli… to… inaczej… fi”,

Następnie po sprawdzeniu liczby argumentów niezerowych, a nawet,

Napisz pętlę while z tymi czterema instrukcjami eval jako ciało, a następnie instrukcję case wykorzystującą dwie wartości określone w każdym przejściu przez pętlę.

Trudna część skryptu została pokazana tutaj:

#!/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
źródło
-3
mitsos@redhat24$ my_script "a=1;b=mitsos;c=karamitsos"
#!/bin/sh
eval "$1"

właśnie wprowadziłeś parametry wiersza poleceń do zakresu skryptu !!

thettalos
źródło
3
Nie działa to ze składnią określoną przez OP; chcą-a 1 -b mitsos -c karamitsos
Michael Mrozek