2011-08-15 12 views
176

wiem, że mogę łatwo dostać umieszczone parametry jak ten w bash:Jak uzyskać argumenty z flagami w skrypcie bash

$0 lub $1

Chcę, aby móc korzystać z opcji flag tak, aby określić do czego wykorzystywany jest każdy parametr:

mysql -u user -h host 

Jaki jest najlepszy sposób, aby uzyskać -u param wartość i -h param wartości przez flagą zamiast według pozycji?

+2

Może być dobrym pomysłem zapytać/sprawdzić na http://unix.stackexchange.com/ oraz – MRR0GERS

+6

google dla "bash getopts" - wiele samouczków. –

+48

@ glenn-jackman: Na pewno google teraz, gdy znam nazwę. Rzecz w google to - zadać pytanie - powinieneś już znać 50% odpowiedzi. – Stann

Odpowiedz

202

Jest to idiom Zwykle używam:

while test $# -gt 0; do 
     case "$1" in 
       -h|--help) 
         echo "$package - attempt to capture frames" 
         echo " " 
         echo "$package [options] application [arguments]" 
         echo " " 
         echo "options:" 
         echo "-h, --help    show brief help" 
         echo "-a, --action=ACTION  specify an action to use" 
         echo "-o, --output-dir=DIR  specify a directory to store output in" 
         exit 0 
         ;; 
       -a) 
         shift 
         if test $# -gt 0; then 
           export PROCESS=$1 
         else 
           echo "no process specified" 
           exit 1 
         fi 
         shift 
         ;; 
       --action*) 
         export PROCESS=`echo $1 | sed -e 's/^[^=]*=//g'` 
         shift 
         ;; 
       -o) 
         shift 
         if test $# -gt 0; then 
           export OUTPUT=$1 
         else 
           echo "no output dir specified" 
           exit 1 
         fi 
         shift 
         ;; 
       --output-dir*) 
         export OUTPUT=`echo $1 | sed -e 's/^[^=]*=//g'` 
         shift 
         ;; 
       *) 
         break 
         ;; 
     esac 
done 

Kluczowe punkty to:

  • $# jest liczbą argumentów
  • pętli while patrzy na wszystko z argumentów dostarczonych, pasujące na ich wartości wewnątrz instrukcji
  • shift odejmuje pierwszą. Możesz przesuwać wiele razy wewnątrz instrukcji case, aby uzyskać wiele wartości.
+2

Co robią skrzynie '--action *' i '--output-dir *'? – Lucio

+1

Po prostu zapisują wartości, które wprowadzają do środowiska. – Flexo

+3

Chyba @ Luuo znaczyło, co to jest "*" za ... – arod

35

getopt jest twoim przyjacielem .. prosty przykład:

function f() { 
TEMP=`getopt --long -o "u:h:" "[email protected]"` 
eval set -- "$TEMP" 
while true ; do 
    case "$1" in 
     -u) 
      user=$2 
      shift 2 
     ;; 
     -h) 
      host=$2 
      shift 2 
     ;; 
     *) 
      break 
     ;; 
    esac 
done; 

echo "user = $user, host = $host" 
} 

f -u myself -h some_host 

Nie powinno być różne przykłady w katalogu/usr/bin.

+1

Szerszy przykład można znaleźć w katalogu '/ usr/share/doc/util-linux/examples', przynajmniej na komputerach z systemem Ubuntu. –

209

Ten przykład wykorzystuje Bash wbudowanego polecenia getopts i jest z Google Shell Style Guide:

verbose='false' 
aflag='' 
bflag='' 
files='' 

while getopts 'abf:v' flag; do 
    case "${flag}" in 
    a) aflag='true' ;; 
    b) bflag='true' ;; 
    f) files="${OPTARG}" ;; 
    v) verbose='true' ;; 
    *) error "Unexpected option ${flag}" ;; 
    esac 
done 

Uwaga: Jeśli postać następuje dwukropek (np f:), oczekuje się, że opcja będzie posiadać argument .

Przykład użycia: ./script -v -a -b -f filename

Korzystanie getopts ma kilka zalet w stosunku do przyjętego odpowiedź:

  • warunkiem natomiast jest dużo bardziej czytelny i pokazuje, co akceptowane są opcje
  • czystsze kod; nie licząc liczbę parametrów i przesuwanie
  • można dołączyć opcje (np -a -b -c-abc)

Jednak duża wadą jest to, że nie obsługuje długich opcji, tylko opcje pojedynczych znaków.

+16

Można się zastanawiać, dlaczego ta odpowiedź, przy użyciu wbudowanego basha, nie jest najwyższa. –

+5

to powinna być zaakceptowana odpowiedź. – woens

+2

Dla potomności: dwukropek po in 'abf: v' oznacza, że ​​-f przyjmuje dodatkowy argument (nazwa pliku w tym walizka). – zahbaz

4

Inną alternatywą byłoby użyć coś jak na poniższym przykładzie, który pozwoliłby na użycie długich --image lub krótkie -i tagów, a także umożliwić skompilowany -i = „Przykład.jpg” lub oddzielny -i example.jpg Metody przekazywania argumentów.

# declaring a couple of associative arrays 
declare -A arguments=(); 
declare -A variables=(); 

# declaring an index integer 
declare -i index=1; 

# any variables you want to use here 
# on the left left side is argument label or key (entered at the command line along with it's value) 
# on the right side is the variable name the value of these arguments should be mapped to. 
# (the examples above show how these are being passed into this script) 
variables["-gu"]="git_user"; 
variables["--git-user"]="git_user"; 
variables["-gb"]="git_branch"; 
variables["--git-branch"]="git_branch"; 
variables["-dbr"]="db_fqdn"; 
variables["--db-redirect"]="db_fqdn"; 
variables["-e"]="environment"; 
variables["--environment"]="environment"; 

# [email protected] here represents all arguments passed in 
for i in "[email protected]" 
do 
    arguments[$index]=$i; 
    prev_index="$(expr $index - 1)"; 

    # this if block does something akin to "where $i contains =" 
    # "%=*" here strips out everything from the = to the end of the argument leaving only the label 
    if [[ $i == *"="* ]] 
    then argument_label=${i%=*} 
    else argument_label=${arguments[$prev_index]} 
    fi 

    # this if block only evaluates to true if the argument label exists in the variables array 
    if [[ -n ${variables[$argument_label]} ]] 
    then 
     # dynamically creating variables names using declare 
     # "#$argument_label=" here strips out the label leaving only the value 
     if [[ $i == *"="* ]] 
      then declare ${variables[$argument_label]}=${i#$argument_label=} 
      else declare ${variables[$argument_label]}=${arguments[$index]} 
     fi 
    fi 

    index=index+1; 
done; 

# then you could simply use the variables like so: 
echo "$git_user"; 
3

Myślę, że serwer byłby prostszym przykładem tego, co chcesz osiągnąć. Nie ma potrzeby korzystania z zewnętrznych narzędzi. Bash zbudował narzędzia, które mogą wykonać zadanie za ciebie.

function DOSOMETHING { 

    while test $# -gt 0; do 
      case "$1" in 
       -first) 
        shift 
        first_argument=$1 
        shift 
        ;; 
       -last) 
        shift 
        last_argument=$1 
        shift 
        ;; 
       *) 
        echo "$1 is not a recognized flag!" 
        return 1; 
        ;; 
      esac 
    done 

    echo "First argument : $first_argument"; 
    echo "Last argument : $last_argument"; 
} 

Umożliwi to korzystanie z flag, więc bez względu na to, w jakiej kolejności mijasz parametry, uzyskasz prawidłowe zachowanie.

Przykład:

DOSOMETHING -last "Adios" -first "Hola" 

wyjściowa:

First argument : Hola 
Last argument : Adios 

Możesz dodać tę funkcję do swojego profilu lub umieścić go wewnątrz skryptu.

Dzięki!

Edit: Zapisz jako aa pliku, a następnie uruchomić go jako yourfile.sh -last "Adios" -first "Hola"

#!/bin/bash 
while test $# -gt 0; do 
      case "$1" in 
       -first) 
        shift 
        first_argument=$1 
        shift 
        ;; 
       -last) 
        shift 
        last_argument=$1 
        shift 
        ;; 
       *) 
        echo "$1 is not a recognized flag!" 
        return 1; 
        ;; 
      esac 
    done 

    echo "First argument : $first_argument"; 
    echo "Last argument : $last_argument"; 
+0

Używam powyższego kodu, a po uruchomieniu nie drukuje niczego. './hello.sh DOSOMETHING -last" Adios "-pierwszy" Hola "' – dinu0101

+0

@ dinu0101 To jest funkcja. Nie skrypt. Powinieneś użyć go jako DOSOMETHING -last "Adios" -pierwszy "Hola" –

+0

Dzięki @Matias. Zrozumiany. jak uruchomić skrypt. – dinu0101

0

Lubię odpowiedź Roberta McMahan jest najlepszy tutaj, gdyż wydaje się, że najłatwiej przekształcić udostępnianych zawierać pliki dowolnych skryptów posługiwać się. Ale wydaje się, że ma ona wadę w linii if [[ -n ${variables[$argument_label]} ]] rzucającej komunikat "zmienne: niepoprawny indeks tablicy". Nie mam przedstawiciela, który mógłby skomentować, i wątpię, czy jest to poprawna "poprawka", ale owija ją: if w if [[ -n $argument_label ]] ; then czyści to.

Oto kod, który znalazłem, jeśli znasz lepszy sposób, proszę dodać komentarz do odpowiedzi Roberta.

Dołącz plik "flags-declares.sh"

# declaring a couple of associative arrays 
declare -A arguments=(); 
declare -A variables=(); 

# declaring an index integer 
declare -i index=1; 

Dołącz plik "flags-arguments.sh"

# [email protected] here represents all arguments passed in 
for i in "[email protected]" 
do 
    arguments[$index]=$i; 
    prev_index="$(expr $index - 1)"; 

    # this if block does something akin to "where $i contains =" 
    # "%=*" here strips out everything from the = to the end of the argument leaving only the label 
    if [[ $i == *"="* ]] 
    then argument_label=${i%=*} 
    else argument_label=${arguments[$prev_index]} 
    fi 

    if [[ -n $argument_label ]] ; then 
    # this if block only evaluates to true if the argument label exists in the variables array 
    if [[ -n ${variables[$argument_label]} ]] ; then 
     # dynamically creating variables names using declare 
     # "#$argument_label=" here strips out the label leaving only the value 
     if [[ $i == *"="* ]] 
     then declare ${variables[$argument_label]}=${i#$argument_label=} 
     else declare ${variables[$argument_label]}=${arguments[$index]} 
     fi 
    fi 
    fi 

    index=index+1; 
done; 

Twój "script.sh"

. bin/includes/flags-declares.sh 

# any variables you want to use here 
# on the left left side is argument label or key (entered at the command line along with it's value) 
# on the right side is the variable name the value of these arguments should be mapped to. 
# (the examples above show how these are being passed into this script) 
variables["-gu"]="git_user"; 
variables["--git-user"]="git_user"; 
variables["-gb"]="git_branch"; 
variables["--git-branch"]="git_branch"; 
variables["-dbr"]="db_fqdn"; 
variables["--db-redirect"]="db_fqdn"; 
variables["-e"]="environment"; 
variables["--environment"]="environment"; 

. bin/includes/flags-arguments.sh 

# then you could simply use the variables like so: 
echo "$git_user"; 
echo "$git_branch"; 
echo "$db_fqdn"; 
echo "$environment"; 
Powiązane problemy