Bash에서 플래그로 인수를 얻는 방법


283

bash에서 다음과 같은 위치 매개 변수를 쉽게 얻을 수 있다는 것을 알고 있습니다.

$0 또는 $1

다음과 같은 플래그 옵션을 사용하여 각 매개 변수가 사용되는 항목을 지정할 수 있기를 원합니다.

mysql -u user -h host

위치가 아닌 깃발로 -u param가치와 -h param가치 를 얻는 가장 좋은 방법은 무엇입니까 ?


2
그것은 좋은 생각에서 이상 확인 / 문의 할 수 있습니다 unix.stackexchange.com 뿐만 아니라
MRR0GERS

8
"bash getopts"에 대한 Google-많은 튜토리얼.
glenn jackman

89
@ glenn-jackman : 이름을 알았으니 이제 확실하게 구글에 올릴 것입니다. 구글에 관한 것은-질문을하는 것입니다-당신은 이미 답의 50 %를 알고 있어야합니다.
Stann

답변:


292

이것은 내가 일반적으로 사용하는 관용구입니다.

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

핵심 사항은 다음과 같습니다.

  • $# 인수의 수
  • while 루프는 제공된 모든 인수를보고 case 문 내에서 값과 일치합니다.
  • 교대는 첫 번째 것을 빼앗아갑니다. case 문 내에서 여러 번 이동하여 여러 값을 취할 수 있습니다.

3
뭐라고 않습니다 --action*--output-dir*경우가 있습니까?
Lucio

1
그들은 환경에 가치를 저장합니다.
Flexo

22
@Lucio Super old comment, 그러나 다른 사람이이 페이지를 방문한 경우 추가하십시오. * (와일드 카드)는 누군가가 타이핑 --action=[ACTION]하는 경우뿐만 아니라 누군가가 타이핑 하는 경우를위한 것입니다--action [ACTION]
cooper

2
*)당신이 거기에서 휴식을 취합니까, 나쁜 옵션을 끝내거나 무시해서는 안됩니까? 즉 -bad -o dir, -o dir부품이 처리되지 않습니다.
newguy 2016 년

@newguy 좋은 질문입니다. 나는 그들이 다른 것에 빠지게하려고했다고 생각한다
Flexo

427

이 예제는 Bash의 내장 getopts명령을 사용하며 Google Shell Style Guide에서 제공됩니다 .

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

참고 : 문자 뒤에 콜론 (예 :)이 오는 경우 f:해당 옵션에 인수가 있어야합니다.

사용법 예 : ./script -v -a -b -f filename

getopts를 사용하면 허용되는 답변보다 몇 가지 장점이 있습니다.

  • while 조건은 훨씬 더 읽기 쉽고 허용되는 옵션이 무엇인지 보여줍니다.
  • 더 깨끗한 코드; 매개 변수의 수를 세지 않고 이동
  • 옵션에 가입 할 수 있습니다 (예 : -a -b -c-abc)

그러나 큰 단점은 긴 옵션을 지원하지 않고 단일 문자 옵션 만 지원한다는 것입니다.


48
bash는 내장을 사용하여이 대답은, 상단 하나없는 이유를 하나 개의 불가사의
윌 Barnwell

13
후손의 경우 : 'abf : v'뒤에있는 콜론은 -f가 추가 인수 (이 경우 파일 이름)를 사용함을 나타냅니다.
zahbaz

1
오류 줄을 다음과 같이 변경해야했습니다.?) printf '\nUsage: %s: [-a] aflag [-b] bflag\n' $0; exit 2 ;;
Andy

7
콜론에 대한 메모를 추가 할 수 있습니까? 각 문자 다음에 콜론이 없다는 것은 인수가 없음을 의미하고 하나의 콜론은 인수를 의미하며 두 개의 콜론은 선택적 인수를 의미합니까?
limasxgoesto0

3
@WillBarnwell은 질문을한지 3 년 후에 추가 된 반면, 가장 큰 답변은 같은 날에 추가되었다는 점에 유의해야합니다.
rbennell

47

getopt는 당신의 친구입니다. 간단한 예 :

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 디렉토리에는 다양한 예제가 있어야합니다.


3
좀 더 광범위한 예제는 /usr/share/doc/util-linux/examples최소한 우분투 머신 의 디렉토리에서 찾을 수 있습니다 .
Sroo Stroobandt

10

나는 이것이 당신이 달성하고자하는 것의 더 간단한 예라고 생각합니다. 외부 도구를 사용할 필요가 없습니다. 내장 된 도구가 작업을 수행 할 수 있습니다.

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

이를 통해 플래그를 사용할 수 있으므로 매개 변수를 전달하는 순서에 관계없이 올바른 동작을 얻게됩니다.

예 :

 DOSOMETHING -last "Adios" -first "Hola"

출력 :

 First argument : Hola
 Last argument : Adios

이 기능을 프로필에 추가하거나 스크립트 안에 넣을 수 있습니다.

감사!

편집 : 이것을 파일로 저장하고 다음으로 실행하십시오. 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";

위의 코드를 사용하고 실행할 때 아무것도 인쇄하지 않습니다. ./hello.sh DOSOMETHING -last "Adios" -first "Hola"
dinu0101

@ dinu0101 이것은 함수입니다. 스크립트가 아닙니다. DOSOMETHING -last "Adios"-first "Hola"로 사용하십시오
Matias Barrios

감사합니다 @Matias. 이해 했어 스크립트 내에서 실행하는 방법.
dinu0101

1
매우 감사합니다 @Matias
dinu0101

2
macOS return 1;에서 마지막 예제 출력과 함께 사용 합니다 can only 'return' from a function or sourced script. 로 전환 exit 1;하지만 예상대로 작동합니다.
Mattias

5

다른 대안은 아래 예제와 같이 long --image 또는 short -i 태그를 사용하고 컴파일 된 -i = "example.jpg" 또는 별도의 -i example.jpg 인수를 전달할 수있는 메소드를 사용하는 것입니다. .

# 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의 답변이 여기에 가장 좋습니다. 모든 스크립트에서 공유 가능한 포함 파일을 만드는 것이 가장 쉬운 것처럼 보입니다. 그러나 if [[ -n ${variables[$argument_label]} ]]"변수 : 잘못된 배열 아래 첨자"라는 메시지를 표시 하는 줄에 결함이있는 것 같습니다 . 나는 의견에 담당자가없는, 나는이 적절한 의심 '수정'하지만 포장 ifif [[ -n $argument_label ]] ; then세척 과정이까지.

더 나은 방법을 알고 있다면 Robert의 답변에 의견을 추가하십시오.

파일 포함 "flags-declares.sh"

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

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

파일 포함 "flags-arguments.sh"

# $@ 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에 익숙하고 bash 인수를 구문 분석하기 위해 python을 호출하는 것을 신경 쓰지 않으면 argparse-bash https://github.com/nhoffman/ 이라는 정말 유용하고 사용하기 쉬운 코드가 있습니다. argparse-bash

예제는 example.sh 스크립트에서 가져옵니다.

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

간단한 TLDR을 제안합니다.; 시작되지 않은 예.

helloworld.sh라는 bash 스크립트를 작성하십시오.

#!/bin/bash

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

echo "Hello $Name!"

그런 다음 -n스크립트를 실행할 때 선택적 매개 변수를 전달할 수 있습니다 .

다음과 같이 스크립트를 실행하십시오.

$ bash helloworld.sh -n 'World'

산출

$ Hello World!

노트

여러 개의 매개 변수를 사용하려는 경우 :

  1. while getops "n:" arg: do더 많은 매개 변수로 확장while getops "n:o:p:" arg: do
  2. 추가 변수 할당으로 케이스 스위치를 확장하십시오. 같은 o) Option=$OPTARGp) Parameter=$OPTARG

1
#!/bin/bash

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

sample.sh로 저장하고 실행 해보십시오

sh sample.sh -n John

터미널에서.


1

여러 플래그와 함께 getopts를 사용하는 데 문제가있어서이 코드를 작성했습니다. 모달 변수를 사용하여 플래그를 감지하고 해당 플래그를 사용하여 변수에 인수를 지정합니다.

플래그에 인수가 없어야하는 경우 CURRENTFLAG 설정 이외의 작업을 수행 할 수 있습니다.

    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

그래서 여기 내 솔루션입니다. 하이픈, 하이픈, 하이픈 및 하이픈 두 개와 함께 하이픈없이 부울 플래그를 처리 할 수 ​​있기를 원했습니다.

# 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

일부 참조

  • 주요 절차는 여기 에서 발견 되었습니다 .
  • 모든 인수를 함수에 전달하는 방법에 대한 자세한 내용은 here .
  • 기본값에 대한 자세한 내용은 여기를 참조하십시오 .
  • declaredo 에 대한 추가 정보 $ bash -c "help declare".
  • shiftdo 에 대한 추가 정보 $ bash -c "help shift".
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.