Bash bayrakları ile nasıl argümanlar elde edilir


283

Ben kolayca bash böyle konumlandırılmış parametreleri alabilirsiniz biliyorum:

$0 veya $1

Her parametrenin ne kullanıldığını belirtmek için böyle bayrak seçeneklerini kullanabilmek istiyorum:

mysql -u user -h host

Konuma göre değil, bayrağa göre -u paramdeğer ve -h paramdeğer elde etmenin en iyi yolu nedir ?


2
Bu iyi bir fikir de üzerinde kontrol / sormak olabilir unix.stackexchange.com yanı
MRR0GERS

8
"bash getopts" için google - çok sayıda öğretici.
glenn jackman

89
@ glenn-jackman: Ben ismini bildiğim için kesinlikle google'a gireceğim. Google ile ilgili şey - bir soru sormak - cevabın% 50'sini zaten bilmelisiniz.
Stann

Yanıtlar:


292

Bu genellikle kullandığım deyimdir:

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

Kilit noktalar:

  • $# argüman sayısı
  • döngü, bir vaka ifadesi içindeki değerleriyle eşleşen, sağlanan tüm bağımsız değişkenlere bakar
  • shift ilkini alır. Birden çok değer almak için bir vaka ifadesinin içinde birden çok kez kaydırabilirsiniz.

3
--action*Ve --output-dir*vakaları ne yapar ?
Lucio

1
Sadece çevreye kazandıkları değerleri saklıyorlar.
Flekso

22
@Lucio Super eski yorum, ancak başka birinin bu sayfayı ziyaret etmesi durumunda ekleyin. * (Joker karakter) --action=[ACTION]birisinin --action [ACTION]
Cooper

2
Neden *)oradan ayrılıyorsun, kötü seçenekten çıkmamalı ya da görmezden gelmemelisin? Başka bir deyişle -bad -o dir, -o dirparça asla işlenmez.
newguy

@newguy iyi bir soru. Sanırım başka bir şeye düşmelerine izin vermeye çalışıyordum
Flexo

428

Bu örnekte Bash'in yerleşik getoptskomutu kullanılmaktadır ve Google Kabuk Stil Kılavuzu'ndan alınmıştır :

a_flag=''
b_flag=''
files=''
verbose='false'

print_usage() {
  printf "Usage: ..."
}

while getopts 'abf:v' flag; do
  case "${flag}" in
    a) a_flag='true' ;;
    b) b_flag='true' ;;
    f) files="${OPTARG}" ;;
    v) verbose='true' ;;
    *) print_usage
       exit 1 ;;
  esac
done

Not: Bir karakteri iki nokta üst üste (örneğin f:) izliyorsa , bu seçeneğin bir argümanı olması beklenir.

Örnek kullanım: ./script -v -a -b -f filename

Getopts kullanmanın kabul edilen cevaba göre birkaç avantajı vardır:

  • while koşulu çok daha okunabilir ve kabul edilen seçeneklerin ne olduğunu gösterir
  • temiz kod; parametre sayısını sayma ve değiştirme
  • seçeneklere katılabilirsiniz (örn. -a -b -c-abc)

Bununla birlikte, büyük bir dezavantaj, uzun seçenekleri değil, sadece tek karakterli seçenekleri desteklemesidir.


48
Biri bir bash yerleşik kullanarak bu cevabın neden birinci olmadığını merak ediyor
Will Barnwell

13
Gelecek kuşak için: 'abf: v' içindeki iki nokta üst üste işareti -f ifadesinin ek bir bağımsız değişken aldığını (bu durumda dosya adı) belirtir.
zahbaz

1
Hata satırını değiştirmek zorunda kaldım:?) printf '\nUsage: %s: [-a] aflag [-b] bflag\n' $0; exit 2 ;;
Andy

7
İki nokta üst üste hakkında bir not ekleyebilir misiniz? Her harfin ardından hiçbir kolon hiçbir arg, bir kolon bir arg ve iki kolon isteğe bağlı arg anlamına gelir?
17:22

3
@WillBarnwell, soru sorulduktan 3 yıl sonra eklenirken en üstteki cevabın aynı gün eklendiğini belirtmek gerekir.
rbennell

47

getopt arkadaşın .. basit bir örnek:

function f () {
TEMP=`getopt --long -o "u:h:" "$@"`
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

/ Usr / bin dizininizde çeşitli örnekler olmalıdır.


3
Daha kapsamlı bir örnek dizinde /usr/share/doc/util-linux/examples, en azından Ubuntu makinelerinde bulunabilir.
Serge Stroobandt

10

Bence bu, başarmak istediğiniz şeyin daha basit bir örneği olacak. Harici aletler kullanmaya gerek yoktur. Bash yerleşik araçlar sizin için işi yapabilir.

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";
 }

Bu, bayrakları kullanmanıza izin verir, böylece parametreleri hangi sırayla geçirirseniz geçirin, doğru davranışı elde edersiniz.

Misal :

 DOSOMETHING -last "Adios" -first "Hola"

Çıktı :

 First argument : Hola
 Last argument : Adios

Bu işlevi profilinize ekleyebilir veya bir komut dosyasının içine koyabilirsiniz.

Teşekkürler!

Düzenleme: Bunu aa dosyası olarak kaydedin ve ardından şu şekilde yürütün 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";

Yukarıdaki kodu kullanır ve ne zaman çalıştırmak bir şey yazdırmıyor. ./hello.sh DOSOMETHING -last "Adios" -first "Hola"
dinu0101

@ dinu0101 Bu bir işlevdir. Senaryo değil. DOSOMETHING -last "Adios" -İlk "Hola" olarak kullanmalısınız
Matias Barrios

Teşekkürler @Matias. Anladım. betiğin içinde nasıl çalıştırılacağı.
dinu0101

1
@Matias
dinu0101

2
MacOS'taki return 1;son örnek çıktılarla kullanma can only 'return' from a function or sourced script. Geçiş exit 1;olsa beklendiği gibi eserleri.
Mattias

5

Başka bir alternatif uzun kullanmasına izin vereceğini örnek aşağıda gibi bir şey kullanmak olacaktır --image veya kısa -i etiketleri ve ayrıca izin derlenmiş -i = "example.jpg" ya ayrı -i example.jpg argümanlar geçen yöntemleri .

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

# $@ here represents all arguments passed in
for i in "$@"  
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

Robert McMahan'ın en iyi yanıtı burada seviyorum, çünkü komut dosyalarınızdan herhangi birinin kullanması için paylaşılabilir dosya eklemenin en kolay yolu gibi görünüyor. Ama satır if [[ -n ${variables[$argument_label]} ]]"" değişkenler: kötü dizi alt simge "mesajını veren bir kusur var gibi görünüyor . Ben yorumuna temsilcisi yok ve bu doğru olduğundan şüpheliyim 'düzeltme' ama bu sarma ifiçinde if [[ -n $argument_label ]] ; thentemizler o kadar.

İşte size daha iyi bir yol biliyorsanız, lütfen Robert'ın cevabına bir yorum ekleyin.

"Flags-declares.sh" Dosyasını İçer

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

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

"Flags-arguments.sh" Dosyasını İçer

# $@ here represents all arguments passed in
for i in "$@"
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;

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

3

Python argparse hakkında bilginiz varsa ve bash argümanlarını ayrıştırmak için python çağırmayı önemsemiyorsanız, argparse-bash adlı gerçekten yararlı ve süper kolay bulduğum bir kod parçası https://github.com/nhoffman/ argparse-bash

Example.sh komut dosyalarından örnek al:

#!/bin/bash

source $(dirname $0)/argparse.bash || exit 1
argparse "$@" <<EOF || exit 1
parser.add_argument('infile')
parser.add_argument('outfile')
parser.add_argument('-a', '--the-answer', default=42, type=int,
                    help='Pick a number [default %(default)s]')
parser.add_argument('-d', '--do-the-thing', action='store_true',
                    default=False, help='store a boolean [default %(default)s]')
parser.add_argument('-m', '--multiple', nargs='+',
                    help='multiple values allowed')
EOF

echo required infile: "$INFILE"
echo required outfile: "$OUTFILE"
echo the answer: "$THE_ANSWER"
echo -n do the thing?
if [[ $DO_THE_THING ]]; then
    echo " yes, do it"
else
    echo " no, do not do it"
fi
echo -n "arg with multiple values: "
for a in "${MULTIPLE[@]}"; do
    echo -n "[$a] "
done
echo

2

Basit bir TLDR öneriyorum :; başlatılmayanlara örnek.

Helloworld.sh adlı bir bash betiği oluşturun

#!/bin/bash

while getopts "n:" arg; do
  case $arg in
    n) Name=$OPTARG;;
  esac
done

echo "Hello $Name!"

Daha sonra -nkomut dosyasını yürütürken isteğe bağlı bir parametre iletebilirsiniz .

Komut dosyasını şu şekilde yürütün:

$ bash helloworld.sh -n 'World'

Çıktı

$ Hello World!

notlar

Birden fazla parametre kullanmak isterseniz:

  1. while getops "n:" arg: dogibi daha fazla paramater ile genişletmekwhile getops "n:o:p:" arg: do
  2. kasa anahtarını ekstra değişken atamalarla uzatın. Gibi o) Option=$OPTARGvep) Parameter=$OPTARG

1
#!/bin/bash

if getopts "n:" arg; then
  echo "Welcome $OPTARG"
fi

Sample.sh olarak kaydedin ve çalıştırmayı deneyin

sh sample.sh -n John

terminalinizde.


1

Birden fazla bayraklı getopts kullanırken sorun yaşadım, bu yüzden bu kodu yazdım. Bayrakları algılamak ve değişkenlere argümanlar atamak için bu bayrakları kullanmak için kalıcı bir değişken kullanır.

Bir bayrağın bağımsız değişkeni olmaması gerekiyorsa, CURRENTFLAG ayarlamak dışında bir şeyin yapılabileceğini unutmayın.

    for MYFIELD in "$@"; do

        CHECKFIRST=`echo $MYFIELD | cut -c1`

        if [ "$CHECKFIRST" == "-" ]; then
            mode="flag"
        else
            mode="arg"
        fi

        if [ "$mode" == "flag" ]; then
            case $MYFIELD in
                -a)
                    CURRENTFLAG="VARIABLE_A"
                    ;;
                -b)
                    CURRENTFLAG="VARIABLE_B"
                    ;;
                -c)
                    CURRENTFLAG="VARIABLE_C"
                    ;;
            esac
        elif [ "$mode" == "arg" ]; then
            case $CURRENTFLAG in
                VARIABLE_A)
                    VARIABLE_A="$MYFIELD"
                    ;;
                VARIABLE_B)
                    VARIABLE_B="$MYFIELD"
                    ;;
                VARIABLE_C)
                    VARIABLE_C="$MYFIELD"
                    ;;
            esac
        fi
    done

0

İşte benim çözümüm. Botanik bayraklarını kısa çizgi olmadan, bir kısa çizgi ile ve iki kısa çizgi ile ve bir ve iki kısa çizgi ile parametre / değer atama ile işlemek istedim.

# Handle multiple types of arguments and prints some variables
#
# Boolean flags
# 1) No hyphen
#    create   Assigns `true` to the variable `CREATE`.
#             Default is `CREATE_DEFAULT`.
#    delete   Assigns true to the variable `DELETE`.
#             Default is `DELETE_DEFAULT`.
# 2) One hyphen
#      a      Assigns `true` to a. Default is `false`.
#      b      Assigns `true` to b. Default is `false`.
# 3) Two hyphens
#    cats     Assigns `true` to `cats`. By default is not set.
#    dogs     Assigns `true` to `cats`. By default is not set.
#
# Parameter - Value
# 1) One hyphen
#      c      Assign any value you want
#      d      Assign any value you want
#
# 2) Two hyphens
#   ... Anything really, whatever two-hyphen argument is given that is not
#       defined as flag, will be defined with the next argument after it.
#
# Example:
# ./parser_example.sh delete -a -c VA_1 --cats --dir /path/to/dir
parser() {
    # Define arguments with one hyphen that are boolean flags
    HYPHEN_FLAGS="a b"
    # Define arguments with two hyphens that are boolean flags
    DHYPHEN_FLAGS="cats dogs"

    # Iterate over all the arguments
    while [ $# -gt 0 ]; do
        # Handle the arguments with no hyphen
        if [[ $1 != "-"* ]]; then
            echo "Argument with no hyphen!"
            echo $1
            # Assign true to argument $1
            declare $1=true
            # Shift arguments by one to the left
            shift
        # Handle the arguments with one hyphen
        elif [[ $1 == "-"[A-Za-z0-9]* ]]; then
            # Handle the flags
            if [[ $HYPHEN_FLAGS == *"${1/-/}"* ]]; then
                echo "Argument with one hyphen flag!"
                echo $1
                # Remove the hyphen from $1
                local param="${1/-/}"
                # Assign true to $param
                declare $param=true
                # Shift by one
                shift
            # Handle the parameter-value cases
            else
                echo "Argument with one hyphen value!"
                echo $1 $2
                # Remove the hyphen from $1
                local param="${1/-/}"
                # Assign argument $2 to $param
                declare $param="$2"
                # Shift by two
                shift 2
            fi
        # Handle the arguments with two hyphens
        elif [[ $1 == "--"[A-Za-z0-9]* ]]; then
            # NOTE: For double hyphen I am using `declare -g $param`.
            #   This is the case because I am assuming that's going to be
            #   the final name of the variable
            echo "Argument with two hypens!"
            # Handle the flags
            if [[ $DHYPHEN_FLAGS == *"${1/--/}"* ]]; then
                echo $1 true
                # Remove the hyphens from $1
                local param="${1/--/}"
                # Assign argument $2 to $param
                declare -g $param=true
                # Shift by two
                shift
            # Handle the parameter-value cases
            else
                echo $1 $2
                # Remove the hyphens from $1
                local param="${1/--/}"
                # Assign argument $2 to $param
                declare -g $param="$2"
                # Shift by two
                shift 2
            fi
        fi

    done
    # Default value for arguments with no hypheb
    CREATE=${create:-'CREATE_DEFAULT'}
    DELETE=${delete:-'DELETE_DEFAULT'}
    # Default value for arguments with one hypen flag
    VAR1=${a:-false}
    VAR2=${b:-false}
    # Default value for arguments with value
    # NOTE1: This is just for illustration in one line. We can well create
    #   another function to handle this. Here I am handling the cases where
    #   we have a full named argument and a contraction of it.
    #   For example `--arg1` can be also set with `-c`.
    # NOTE2: What we are doing here is to check if $arg is defined. If not,
    #   check if $c was defined. If not, assign the default value "VD_"
    VAR3=$(if [[ $arg1 ]]; then echo $arg1; else echo ${c:-"VD_1"}; fi)
    VAR4=$(if [[ $arg2 ]]; then echo $arg2; else echo ${d:-"VD_2"}; fi)
}


# Pass all the arguments given to the script to the parser function
parser "$@"


echo $CREATE $DELETE $VAR1 $VAR2 $VAR3 $VAR4 $cats $dir

Bazı referanslar

  • Ana prosedür burada bulundu .
  • Tüm argümanları burada bir fonksiyona aktarmakla ilgili daha fazla bilgi .
  • Varsayılan değerlerle ilgili daha fazla bilgiyi burada bulabilirsiniz .
  • declareDo hakkında daha fazla bilgi $ bash -c "help declare".
  • shiftDo hakkında daha fazla bilgi $ bash -c "help shift".
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.