문제

다음 줄로 호출되는 스크립트가 있습니다.

./myscript -vfd ./foo/bar/someFile -o /fizz/someOtherFile

아니면 이거:

./myscript -v -f -d -o /fizz/someOtherFile ./foo/bar/someFile 

각 경우(또는 둘의 조합)에서 이를 구문 분석하는 데 허용되는 방법은 무엇입니까? $v, $f, 그리고 $d 모두 다음과 같이 설정됩니다. true 그리고 $outFile 와 같을 것이다 /fizz/someOtherFile ?

도움이 되었습니까?

해결책

업데이트: 이 답변을 시작한 지 벌써 5년이 넘었습니다.많은 훌륭한 수정/의견/제안을 보내주셔서 감사합니다.유지 관리 시간을 절약하기 위해 코드 블록을 100% 복사-붙여넣기가 가능하도록 수정했습니다."X를 Y로 바꾸면 어떨까요…"와 같은 댓글을 게시하지 마세요.대신, 코드 블록을 복사하여 붙여넣고, 출력을 확인하고, 변경하고, 스크립트를 다시 실행하고, "X를 Y로 변경했고..."라고 코멘트를 작성하세요. 아이디어를 테스트하고 작동하는지 말할 시간이 없습니다.


방법 #1:getopt[s] 없이 bash 사용

키-값 쌍 인수를 전달하는 두 가지 일반적인 방법은 다음과 같습니다.

Bash 공백으로 구분(예: --option argument) (getopt[s] 제외)

용법 demo-space-separated.sh -e conf -s /etc -l /usr/lib /etc/hosts

cat >/tmp/demo-space-separated.sh <<'EOF'
#!/bin/bash

POSITIONAL=()
while [[ $# -gt 0 ]]
do
key="$1"

case $key in
    -e|--extension)
    EXTENSION="$2"
    shift # past argument
    shift # past value
    ;;
    -s|--searchpath)
    SEARCHPATH="$2"
    shift # past argument
    shift # past value
    ;;
    -l|--lib)
    LIBPATH="$2"
    shift # past argument
    shift # past value
    ;;
    --default)
    DEFAULT=YES
    shift # past argument
    ;;
    *)    # unknown option
    POSITIONAL+=("$1") # save it in an array for later
    shift # past argument
    ;;
esac
done
set -- "${POSITIONAL[@]}" # restore positional parameters

echo "FILE EXTENSION  = ${EXTENSION}"
echo "SEARCH PATH     = ${SEARCHPATH}"
echo "LIBRARY PATH    = ${LIBPATH}"
echo "DEFAULT         = ${DEFAULT}"
echo "Number files in SEARCH PATH with EXTENSION:" $(ls -1 "${SEARCHPATH}"/*."${EXTENSION}" | wc -l)
if [[ -n $1 ]]; then
    echo "Last line of file specified as non-opt/last argument:"
    tail -1 "$1"
fi
EOF

chmod +x /tmp/demo-space-separated.sh

/tmp/demo-space-separated.sh -e conf -s /etc -l /usr/lib /etc/hosts

위 블록을 복사하여 붙여넣은 결과:

FILE EXTENSION  = conf
SEARCH PATH     = /etc
LIBRARY PATH    = /usr/lib
DEFAULT         =
Number files in SEARCH PATH with EXTENSION: 14
Last line of file specified as non-opt/last argument:
#93.184.216.34    example.com

Bash는 같음으로 구분됩니다(예: --option=argument) (getopt[s] 제외)

용법 demo-equals-separated.sh -e=conf -s=/etc -l=/usr/lib /etc/hosts

cat >/tmp/demo-equals-separated.sh <<'EOF'
#!/bin/bash

for i in "$@"
do
case $i in
    -e=*|--extension=*)
    EXTENSION="${i#*=}"
    shift # past argument=value
    ;;
    -s=*|--searchpath=*)
    SEARCHPATH="${i#*=}"
    shift # past argument=value
    ;;
    -l=*|--lib=*)
    LIBPATH="${i#*=}"
    shift # past argument=value
    ;;
    --default)
    DEFAULT=YES
    shift # past argument with no value
    ;;
    *)
          # unknown option
    ;;
esac
done
echo "FILE EXTENSION  = ${EXTENSION}"
echo "SEARCH PATH     = ${SEARCHPATH}"
echo "LIBRARY PATH    = ${LIBPATH}"
echo "DEFAULT         = ${DEFAULT}"
echo "Number files in SEARCH PATH with EXTENSION:" $(ls -1 "${SEARCHPATH}"/*."${EXTENSION}" | wc -l)
if [[ -n $1 ]]; then
    echo "Last line of file specified as non-opt/last argument:"
    tail -1 $1
fi
EOF

chmod +x /tmp/demo-equals-separated.sh

/tmp/demo-equals-separated.sh -e=conf -s=/etc -l=/usr/lib /etc/hosts

위 블록을 복사하여 붙여넣은 결과:

FILE EXTENSION  = conf
SEARCH PATH     = /etc
LIBRARY PATH    = /usr/lib
DEFAULT         =
Number files in SEARCH PATH with EXTENSION: 14
Last line of file specified as non-opt/last argument:
#93.184.216.34    example.com

더 잘 이해하려면 ${i#*=} 에서 "하위 문자열 제거"를 검색하세요. 이 가이드.기능적으로는 다음과 같습니다. `sed 's/[^=]*=//' <<< "$i"` 불필요한 하위 프로세스를 호출하거나 `echo "$i" | sed 's/[^=]*=//'` 어느 전화 불필요한 하위 프로세스.

방법 #2:getopt[s]와 함께 bash 사용

에서: http://mywiki.wooledge.org/BashFAQ/035#getopts

getopt(1) 제한사항 (오래된, 비교적 최근의 getopt 버전):

  • 빈 문자열인 인수를 처리할 수 없습니다.
  • 공백이 포함된 인수를 처리할 수 없습니다.

더 최근의 getopt 버전에는 이러한 제한이 없습니다.

또한 POSIX 셸(및 기타)은 다음을 제공합니다. getopts 이러한 제한이 없습니다.간단한 내용을 포함시켰습니다 getopts 예.

용법 demo-getopts.sh -vf /etc/hosts foo bar

cat >/tmp/demo-getopts.sh <<'EOF'
#!/bin/sh

# A POSIX variable
OPTIND=1         # Reset in case getopts has been used previously in the shell.

# Initialize our own variables:
output_file=""
verbose=0

while getopts "h?vf:" opt; do
    case "$opt" in
    h|\?)
        show_help
        exit 0
        ;;
    v)  verbose=1
        ;;
    f)  output_file=$OPTARG
        ;;
    esac
done

shift $((OPTIND-1))

[ "${1:-}" = "--" ] && shift

echo "verbose=$verbose, output_file='$output_file', Leftovers: $@"
EOF

chmod +x /tmp/demo-getopts.sh

/tmp/demo-getopts.sh -vf /etc/hosts foo bar

위 블록을 복사하여 붙여넣은 결과:

verbose=1, output_file='/etc/hosts', Leftovers: foo bar

장점 getopts 이다:

  1. 이식성이 뛰어나고 다음과 같은 다른 쉘에서도 작동합니다. dash.
  2. 다음과 같은 여러 단일 옵션을 처리할 수 있습니다. -vf filename 일반적인 Unix 방식으로 자동으로.

단점은 getopts 짧은 옵션만 처리할 수 있다는 것입니다(-h, 아니다 --help) 추가 코드 없이.

이있다 getopt 튜토리얼 모든 구문과 변수의 의미를 설명합니다.bash에는 다음도 있습니다. help getopts, 이는 유익할 수 있습니다.

다른 팁

답변 언급 없음 향상된 getopt.그리고 가장 많은 투표를 받은 답변 오해의 소지가 있습니다: 무시하거나 -⁠vfd 스타일 짧은 옵션(OP에서 요청) 또는 위치 인수 뒤의 옵션(OP에서도 요청)구문 분석 오류를 무시합니다.대신에:

  • 향상된 사용 getopt util-linux 또는 이전 GNU glibc에서.1
  • 그것은 함께 작동합니다 getopt_long() GNU glibc의 C 함수.
  • 가지다 모두 유용한 구별 기능(다른 기능에는 없음):
    • 공백, 인용 문자, 인수의 이진수까지 처리합니다.2 (비향상 getopt 이건 할 수 없어)
    • 마지막에 옵션을 처리할 수 있습니다. script.sh -o outFile file1 file2 -v (getopts 이러지 않는다)
    • 허용한다 =-스타일 긴 옵션: script.sh --outfile=fileOut --infile fileIn (자체 구문 분석의 경우 둘 다 허용하면 시간이 오래 걸립니다)
    • 결합된 짧은 옵션을 허용합니다. 예: -vfd (자체 구문 분석의 경우 실제 작업)
    • 옵션 인수를 터치할 수 있습니다. -oOutfile 또는 -vfdoOutfile
  • 벌써 너무 늙었어3 어떤 GNU 시스템도 이것을 놓치고 있지 않습니다(예:모든 Linux에 있습니다).
  • 다음을 사용하여 존재 여부를 테스트할 수 있습니다. getopt --test → 반환값 4.
  • 다른 getopt 또는 쉘 내장 getopts 제한적으로 사용됩니다.

다음 호출

myscript -vfd ./foo/bar/someFile -o /fizz/someOtherFile
myscript -v -f -d -o/fizz/someOtherFile -- ./foo/bar/someFile
myscript --verbose --force --debug ./foo/bar/someFile -o/fizz/someOtherFile
myscript --output=/fizz/someOtherFile ./foo/bar/someFile -vfd
myscript ./foo/bar/someFile -df -v --output /fizz/someOtherFile

모두 돌아오다

verbose: y, force: y, debug: y, in: ./foo/bar/someFile, out: /fizz/someOtherFile

다음과 함께 myscript

#!/bin/bash
# saner programming env: these switches turn some bugs into errors
set -o errexit -o pipefail -o noclobber -o nounset

# -allow a command to fail with !’s side effect on errexit
# -use return value from ${PIPESTATUS[0]}, because ! hosed $?
! getopt --test > /dev/null 
if [[ ${PIPESTATUS[0]} -ne 4 ]]; then
    echo 'I’m sorry, `getopt --test` failed in this environment.'
    exit 1
fi

OPTIONS=dfo:v
LONGOPTS=debug,force,output:,verbose

# -regarding ! and PIPESTATUS see above
# -temporarily store output to be able to check for errors
# -activate quoting/enhanced mode (e.g. by writing out “--options”)
# -pass arguments only via   -- "$@"   to separate them correctly
! PARSED=$(getopt --options=$OPTIONS --longoptions=$LONGOPTS --name "$0" -- "$@")
if [[ ${PIPESTATUS[0]} -ne 0 ]]; then
    # e.g. return value is 1
    #  then getopt has complained about wrong arguments to stdout
    exit 2
fi
# read getopt’s output this way to handle the quoting right:
eval set -- "$PARSED"

d=n f=n v=n outFile=-
# now enjoy the options in order and nicely split until we see --
while true; do
    case "$1" in
        -d|--debug)
            d=y
            shift
            ;;
        -f|--force)
            f=y
            shift
            ;;
        -v|--verbose)
            v=y
            shift
            ;;
        -o|--output)
            outFile="$2"
            shift 2
            ;;
        --)
            shift
            break
            ;;
        *)
            echo "Programming error"
            exit 3
            ;;
    esac
done

# handle non-option arguments
if [[ $# -ne 1 ]]; then
    echo "$0: A single input file is required."
    exit 4
fi

echo "verbose: $v, force: $f, debug: $d, in: $1, out: $outFile"

1 향상된 getopt는 Cygwin을 포함한 대부분의 "bash 시스템"에서 사용할 수 있습니다.OS X에서 시도해 보세요 양조 설치 gnu-getopt 또는 sudo port install getopt
2 POSIX exec() 규칙에는 명령줄 인수에 바이너리 NULL을 전달하는 안정적인 방법이 없습니다.해당 바이트는 인수를 조기에 종료합니다.
3 첫 번째 버전은 1997년 또는 그 이전에 출시되었습니다(1997년까지만 추적했습니다).

에서 : DigitalPeer.com 약간의 수정으로

용법 myscript.sh -p=my_prefix -s=dirname -l=libname

#!/bin/bash
for i in "$@"
do
case $i in
    -p=*|--prefix=*)
    PREFIX="${i#*=}"

    ;;
    -s=*|--searchpath=*)
    SEARCHPATH="${i#*=}"
    ;;
    -l=*|--lib=*)
    DIR="${i#*=}"
    ;;
    --default)
    DEFAULT=YES
    ;;
    *)
            # unknown option
    ;;
esac
done
echo PREFIX = ${PREFIX}
echo SEARCH PATH = ${SEARCHPATH}
echo DIRS = ${DIR}
echo DEFAULT = ${DEFAULT}

더 잘 이해하기 위해 ${i#*=} "서브 스트링 제거"를 검색하십시오 이 안내서. 기능적으로 동일합니다 `sed 's/[^=]*=//' <<< "$i"` 불필요한 하위 프로세스 또는 `echo "$i" | sed 's/[^=]*=//'` 어떤 전화 불필요한 하위 프로세스.

getopt()/getopts() 좋은 선택입니다. 도난당했습니다 여기:

"getopt"의 간단한 사용은이 미니 스크립트에 표시됩니다.

#!/bin/bash
echo "Before getopt"
for i
do
  echo $i
done
args=`getopt abc:d $*`
set -- $args
echo "After getopt"
for i
do
  echo "-->$i"
done

우리가 말한 것은 -a, -b, -c 또는 -d 중 하나가 허용되지만 -C는 인수가 이어진다는 것입니다 ( "C :"

우리가 이것을 "g"라고 부르면 시도해 보면 :

bash-2.05a$ ./g -abc foo
Before getopt
-abc
foo
After getopt
-->-a
-->-b
-->-c
-->foo
-->--

우리는 두 가지 논쟁으로 시작하여 "getopt"는 옵션을 분리하고 각각의 주장에 넣습니다. 또한 "-"를 추가했습니다.

더 간결한 방법

script.sh

#!/bin/bash

while [[ "$#" -gt 0 ]]; do case $1 in
  -d|--deploy) deploy="$2"; shift;;
  -u|--uglify) uglify=1;;
  *) echo "Unknown parameter passed: $1"; exit 1;;
esac; shift; done

echo "Should deploy? $deploy"
echo "Should uglify? $uglify"

용법:

./script.sh -d dev -u

# OR:

./script.sh --deploy dev --uglify

무시할 또 다른 예를 추가 할 위험이 있으므로 여기에 제 계획이 있습니다.

  • 손잡이 -n arg 그리고 --name=arg
  • 마지막에 인수를 허용합니다
  • 잘못된 것이 있으면 제정신 오류가 표시됩니다
  • 호환성, bashisms를 사용하지 않습니다
  • 읽기 가능하고 루프에서 상태를 유지할 필요가 없습니다

누군가에게 유용하기를 바랍니다.

while [ "$#" -gt 0 ]; do
  case "$1" in
    -n) name="$2"; shift 2;;
    -p) pidfile="$2"; shift 2;;
    -l) logfile="$2"; shift 2;;

    --name=*) name="${1#*=}"; shift 1;;
    --pidfile=*) pidfile="${1#*=}"; shift 1;;
    --logfile=*) logfile="${1#*=}"; shift 1;;
    --name|--pidfile|--logfile) echo "$1 requires an argument" >&2; exit 1;;

    -*) echo "unknown option: $1" >&2; exit 1;;
    *) handle_argument "$1"; shift 1;;
  esac
done

나는이 질문에 약 4 년 늦었지만 돌려주고 싶습니다. 이전 답변을 시작점으로 사용하여 오래된 Adhoc Param Parsing을 정리했습니다. 그런 다음 다음 템플릿 코드를 리팩토링했습니다. 길고 짧은 매개 변수, 사용 = 또는 공간 분리 된 인수와 함께 그룹화 된 여러 짧은 매개 변수를 모두 처리합니다. 마지막으로 비석이 아닌 인수를 $ 1, $ 2 .. 변수로 다시 연결합니다. 유용하기를 바랍니다.

#!/usr/bin/env bash

# NOTICE: Uncomment if your script depends on bashisms.
#if [ -z "$BASH_VERSION" ]; then bash $0 $@ ; exit $? ; fi

echo "Before"
for i ; do echo - $i ; done


# Code template for parsing command line parameters using only portable shell
# code, while handling both long and short params, handling '-f file' and
# '-f=file' style param data and also capturing non-parameters to be inserted
# back into the shell positional parameters.

while [ -n "$1" ]; do
        # Copy so we can modify it (can't modify $1)
        OPT="$1"
        # Detect argument termination
        if [ x"$OPT" = x"--" ]; then
                shift
                for OPT ; do
                        REMAINS="$REMAINS \"$OPT\""
                done
                break
        fi
        # Parse current opt
        while [ x"$OPT" != x"-" ] ; do
                case "$OPT" in
                        # Handle --flag=value opts like this
                        -c=* | --config=* )
                                CONFIGFILE="${OPT#*=}"
                                shift
                                ;;
                        # and --flag value opts like this
                        -c* | --config )
                                CONFIGFILE="$2"
                                shift
                                ;;
                        -f* | --force )
                                FORCE=true
                                ;;
                        -r* | --retry )
                                RETRY=true
                                ;;
                        # Anything unknown is recorded for later
                        * )
                                REMAINS="$REMAINS \"$OPT\""
                                break
                                ;;
                esac
                # Check for multiple short options
                # NOTICE: be sure to update this pattern to match valid options
                NEXTOPT="${OPT#-[cfr]}" # try removing single short opt
                if [ x"$OPT" != x"$NEXTOPT" ] ; then
                        OPT="-$NEXTOPT"  # multiple short opts, keep going
                else
                        break  # long form, exit inner loop
                fi
        done
        # Done with that param. move to next
        shift
done
# Set the non-parameters back into the positional parameters ($1 $2 ..)
eval set -- $REMAINS


echo -e "After: \n configfile='$CONFIGFILE' \n force='$FORCE' \n retry='$RETRY' \n remains='$REMAINS'"
for i ; do echo - $i ; done

내 대답은 주로 기반을두고 있습니다 Bruno Bronosky의 답변, 그러나 나는 그의 두 개의 순수한 bash 구현을 꽤 자주 사용하는 두 개의 순수한 배쉬 구현을 으깨는 것입니다.

# As long as there is at least one more argument, keep looping
while [[ $# -gt 0 ]]; do
    key="$1"
    case "$key" in
        # This is a flag type option. Will catch either -f or --foo
        -f|--foo)
        FOO=1
        ;;
        # Also a flag type option. Will catch either -b or --bar
        -b|--bar)
        BAR=1
        ;;
        # This is an arg value type option. Will catch -o value or --output-file value
        -o|--output-file)
        shift # past the key and to the value
        OUTPUTFILE="$1"
        ;;
        # This is an arg=value type option. Will catch -o=value or --output-file=value
        -o=*|--output-file=*)
        # No need to shift here since the value is part of the same string
        OUTPUTFILE="${key#*=}"
        ;;
        *)
        # Do whatever you want with extra options
        echo "Unknown option '$key'"
        ;;
    esac
    # Shift after checking all the cases to get the next option
    shift
done

이를 통해 공간 분리 옵션/값과 동일한 정의 값을 모두 가질 수 있습니다.

따라서 다음을 사용하여 스크립트를 실행할 수 있습니다.

./myscript --foo -b -o /fizz/file.txt

만큼 잘:

./myscript -f --bar -o=/fizz/file.txt

그리고 둘 다 동일한 최종 결과를 가져야합니다.

장점 :

  • -arg = 값 및 -arg 값을 모두 허용합니다

  • Bash에서 사용할 수있는 Arg 이름으로 작동합니다.

    • 의미 -a 또는 -arg 또는 -arg 또는 -arg 또는 무엇이든
  • 순수한 배쉬. getopt 또는 getopts를 배우거나 사용할 필요가 없습니다

단점 :

  • Args를 결합 할 수 없습니다

    • 없음 -ABC를 의미합니다. 당신은 -a -b -c를해야합니다

이것들은 내가 내 머리 위에서 생각할 수있는 유일한 장단점입니다.

나는 스크립트에 휴대용 구문 분석을 작성하는 것이 문제를 발견했습니다. Argbash - 스크립트에 대한 인수 정당 코드를 생성 할 수있는 Foss 코드 생성기와 몇 가지 좋은 기능이 있습니다.

https://argbash.io

나는 이것이 사용하기에 충분히 간단하다고 생각합니다.

#!/bin/bash
#

readopt='getopts $opts opt;rc=$?;[ $rc$opt == 0? ]&&exit 1;[ $rc == 0 ]||{ shift $[OPTIND-1];false; }'

opts=vfdo:

# Enumerating options
while eval $readopt
do
    echo OPT:$opt ${OPTARG+OPTARG:$OPTARG}
done

# Enumerating arguments
for arg
do
    echo ARG:$arg
done

호출 예 :

./myscript -v -do /fizz/someOtherFile -f ./foo/bar/someFile
OPT:v 
OPT:d 
OPT:o OPTARG:/fizz/someOtherFile
OPT:f 
ARG:./foo/bar/someFile

@guneysus의 훌륭한 답변을 확장하면서 사용자가 원하는 구문을 사용할 수있는 조정이 있습니다.

command -x=myfilename.ext --another_switch 

vs

command -x myfilename.ext --another_switch

즉, 평등은 공백으로 대체 될 수 있습니다.

이 "퍼지 해석"은 당신의 취향에 맞지 않을 수도 있지만, 다른 유틸리티와 상호 교환 할 수있는 스크립트를 만드는 경우 (FFMPEG에서 작동 해야하는 내 경우와 마찬가지로) 유연성이 유용합니다.

STD_IN=0

prefix=""
key=""
value=""
for keyValue in "$@"
do
  case "${prefix}${keyValue}" in
    -i=*|--input_filename=*)  key="-i";     value="${keyValue#*=}";; 
    -ss=*|--seek_from=*)      key="-ss";    value="${keyValue#*=}";;
    -t=*|--play_seconds=*)    key="-t";     value="${keyValue#*=}";;
    -|--stdin)                key="-";      value=1;;
    *)                                      value=$keyValue;;
  esac
  case $key in
    -i) MOVIE=$(resolveMovie "${value}");  prefix=""; key="";;
    -ss) SEEK_FROM="${value}";          prefix=""; key="";;
    -t)  PLAY_SECONDS="${value}";           prefix=""; key="";;
    -)   STD_IN=${value};                   prefix=""; key="";; 
    *)   prefix="${keyValue}=";;
  esac
done

GetOpts는 #1을 설치 한 경우 훌륭하게 작동하고 #2 같은 플랫폼에서 실행하려고합니다. OSX와 Linux (예를 들어)는이 점에서 다르게 행동합니다.

다음은 평등, 비평가 및 부울 깃발을 지원하는 (비 GetOpts) 솔루션입니다. 예를 들어 이런 식으로 스크립트를 실행할 수 있습니다.

./script --arg1=value1 --arg2 value2 --shouldClean

# parse the arguments.
COUNTER=0
ARGS=("$@")
while [ $COUNTER -lt $# ]
do
    arg=${ARGS[$COUNTER]}
    let COUNTER=COUNTER+1
    nextArg=${ARGS[$COUNTER]}

    if [[ $skipNext -eq 1 ]]; then
        echo "Skipping"
        skipNext=0
        continue
    fi

    argKey=""
    argVal=""
    if [[ "$arg" =~ ^\- ]]; then
        # if the format is: -key=value
        if [[ "$arg" =~ \= ]]; then
            argVal=$(echo "$arg" | cut -d'=' -f2)
            argKey=$(echo "$arg" | cut -d'=' -f1)
            skipNext=0

        # if the format is: -key value
        elif [[ ! "$nextArg" =~ ^\- ]]; then
            argKey="$arg"
            argVal="$nextArg"
            skipNext=1

        # if the format is: -key (a boolean flag)
        elif [[ "$nextArg" =~ ^\- ]] || [[ -z "$nextArg" ]]; then
            argKey="$arg"
            argVal=""
            skipNext=0
        fi
    # if the format has not flag, just a value.
    else
        argKey=""
        argVal="$arg"
        skipNext=0
    fi

    case "$argKey" in 
        --source-scmurl)
            SOURCE_URL="$argVal"
        ;;
        --dest-scmurl)
            DEST_URL="$argVal"
        ;;
        --version-num)
            VERSION_NUM="$argVal"
        ;;
        -c|--clean)
            CLEAN_BEFORE_START="1"
        ;;
        -h|--help|-help|--h)
            showUsage
            exit
        ;;
    esac
done

나는 당신에게 기능을 제공합니다 parse_params 명령 줄에서 매개 변수를 구문 분석합니다.

  1. 순수한 배쉬 솔루션이며 추가 유틸리티가 없습니다.
  2. 글로벌 범위를 오염시키지 않습니다.
  3. 더 많은 로직을 구축 할 수 있도록 변수를 사용하는 것이 간단하게 반환됩니다.
  4. 매개 변수 이전의 대시 양은 중요하지 않습니다 (--all 동등합니다 -all 동등합니다 all=all)

아래 스크립트는 사본-페이스트 작업 데모입니다. 보다 show_use 사용 방법을 이해하는 기능 parse_params.

제한 사항 :

  1. 공간 구분 된 매개 변수를 지원하지 않습니다 (-d 1)
  2. 매개 변수 이름은 대시를 잃게됩니다 --any-param 그리고 -anyparam 동등합니다
  3. eval $(parse_params "$@") bash 내부에서 사용해야합니다 기능 (글로벌 범위에서는 작동하지 않습니다)

#!/bin/bash

# Universal Bash parameter parsing
# Parse equal sign separated params into named local variables
# Standalone named parameter value will equal its param name (--force creates variable $force=="force")
# Parses multi-valued named params into an array (--path=path1 --path=path2 creates ${path[*]} array)
# Puts un-named params as-is into ${ARGV[*]} array
# Additionally puts all named params as-is into ${ARGN[*]} array
# Additionally puts all standalone "option" params as-is into ${ARGO[*]} array
# @author Oleksii Chekulaiev
# @version v1.4.1 (Jul-27-2018)
parse_params ()
{
    local existing_named
    local ARGV=() # un-named params
    local ARGN=() # named params
    local ARGO=() # options (--params)
    echo "local ARGV=(); local ARGN=(); local ARGO=();"
    while [[ "$1" != "" ]]; do
        # Escape asterisk to prevent bash asterisk expansion, and quotes to prevent string breakage
        _escaped=${1/\*/\'\"*\"\'}
        _escaped=${_escaped//\'/\\\'}
        _escaped=${_escaped//\"/\\\"}
        # If equals delimited named parameter
        nonspace="[^[:space:]]"
        if [[ "$1" =~ ^${nonspace}${nonspace}*=..* ]]; then
            # Add to named parameters array
            echo "ARGN+=('$_escaped');"
            # key is part before first =
            local _key=$(echo "$1" | cut -d = -f 1)
            # Just add as non-named when key is empty or contains space
            if [[ "$_key" == "" || "$_key" =~ " " ]]; then
                echo "ARGV+=('$_escaped');"
                shift
                continue
            fi
            # val is everything after key and = (protect from param==value error)
            local _val="${1/$_key=}"
            # remove dashes from key name
            _key=${_key//\-}
            # skip when key is empty
            # search for existing parameter name
            if (echo "$existing_named" | grep "\b$_key\b" >/dev/null); then
                # if name already exists then it's a multi-value named parameter
                # re-declare it as an array if needed
                if ! (declare -p _key 2> /dev/null | grep -q 'declare \-a'); then
                    echo "$_key=(\"\$$_key\");"
                fi
                # append new value
                echo "$_key+=('$_val');"
            else
                # single-value named parameter
                echo "local $_key='$_val';"
                existing_named=" $_key"
            fi
        # If standalone named parameter
        elif [[ "$1" =~ ^\-${nonspace}+ ]]; then
            # remove dashes
            local _key=${1//\-}
            # Just add as non-named when key is empty or contains space
            if [[ "$_key" == "" || "$_key" =~ " " ]]; then
                echo "ARGV+=('$_escaped');"
                shift
                continue
            fi
            # Add to options array
            echo "ARGO+=('$_escaped');"
            echo "local $_key=\"$_key\";"
        # non-named parameter
        else
            # Escape asterisk to prevent bash asterisk expansion
            _escaped=${1/\*/\'\"*\"\'}
            echo "ARGV+=('$_escaped');"
        fi
        shift
    done
}

#--------------------------- DEMO OF THE USAGE -------------------------------

show_use ()
{
    eval $(parse_params "$@")
    # --
    echo "${ARGV[0]}" # print first unnamed param
    echo "${ARGV[1]}" # print second unnamed param
    echo "${ARGN[0]}" # print first named param
    echo "${ARG0[0]}" # print first option param (--force)
    echo "$anyparam"  # print --anyparam value
    echo "$k"         # print k=5 value
    echo "${multivalue[0]}" # print first value of multi-value
    echo "${multivalue[1]}" # print second value of multi-value
    [[ "$force" == "force" ]] && echo "\$force is set so let the force be with you"
}

show_use "param 1" --anyparam="my value" param2 k=5 --force --multi-value=test1 --multi-value=test2

이것은 스택에서 더 높은 곳에서 getopts가 실행되는 것을 피하기 위해 함수에서 수행하는 방법입니다.

function waitForWeb () {
   local OPTIND=1 OPTARG OPTION
   local host=localhost port=8080 proto=http
   while getopts "h:p:r:" OPTION; do
      case "$OPTION" in
      h)
         host="$OPTARG"
         ;;
      p)
         port="$OPTARG"
         ;;
      r)
         proto="$OPTARG"
         ;;
      esac
   done
...
}

EasyOptions 구문 분석이 필요하지 않습니다.

## Options:
##   --verbose, -v  Verbose mode
##   --output=FILE  Output filename

source easyoptions || exit

if test -n "${verbose}"; then
    echo "output file is ${output}"
    echo "${arguments[@]}"
fi

옵션 파싱 버전을 제공하고 싶습니다.

-s p1
--stage p1
-w somefolder
--workfolder somefolder
-sw p1 somefolder
-e=hello

또한이를 허용합니다 (원치 않을 수 있음).

-s--workfolder p1 somefolder
-se=hello p1
-swe=hello p1 somefolder

if = 옵션에서 사용하기 전에 결정해야합니다. 이것은 코드를 깨끗하게 유지하는 것입니다 (ISH).

while [[ $# > 0 ]]
do
    key="$1"
    while [[ ${key+x} ]]
    do
        case $key in
            -s*|--stage)
                STAGE="$2"
                shift # option has parameter
                ;;
            -w*|--workfolder)
                workfolder="$2"
                shift # option has parameter
                ;;
            -e=*)
                EXAMPLE="${key#*=}"
                break # option has been fully handled
                ;;
            *)
                # unknown option
                echo Unknown option: $key #1>&2
                exit 10 # either this: my preferred way to handle unknown options
                break # or this: do this to signal the option has been handled (if exit isn't used)
                ;;
        esac
        # prepare for next option in this key, if any
        [[ "$key" = -? || "$key" == --* ]] && unset key || key="${key/#-?/-}"
    done
    shift # option(s) fully processed, proceed to next input argument
done

주목하십시오 getopt(1) AT & T의 짧은 살아있는 실수였습니다.

getopt는 1984 년에 만들어졌지만 실제로는 사용할 수 없었기 때문에 1986 년에 이미 묻혔습니다.

사실에 대한 증거 getopt 매우 구식입니다 getopt(1) Man Page는 여전히 언급합니다 "$*" 대신에 "$@", 그것은 1986 년 Bourne Shell에 추가되었습니다. getopts(1) 내부의 공간과 논쟁을 처리하기 위해 쉘이 내장되었습니다.

BTW : 쉘 스크립트에서 긴 옵션을 구문 분석하는 데 관심이 있다면 getopt(3) LIBC (Solaris) 및 ksh93 둘 다 짧은 옵션의 별칭으로 긴 옵션을 지원하는 균일 한 긴 옵션 구현을 추가했습니다. 이것은 원인입니다 ksh93 그리고 Bourne Shell 긴 옵션을 통해 균일 한 인터페이스를 구현합니다 getopts.

Bourne Shell Man 페이지에서 가져온 긴 옵션의 예 :

getopts "f:(file)(input-file)o:(output-file)" OPTX "$@"

Bourne Shell과 KSH93에서 옵션 별칭이 얼마나 오래 사용할 수 있는지를 보여줍니다.

최근 Bourne Shell의 남자 페이지를 참조하십시오.

http://schillix.sourceforge.net/man/man1/bosh.1.html

그리고 Opensolaris의 getopt (3)의 Man Page :

http://schillix.sourceforge.net/man/man3c/getopt.3c.html

마지막으로, 구식 $*를 확인하기위한 getOpt (1) Man Page.

http://schillix.sourceforge.net/man/man1/getopt.1.html

도대치되지 않은 논쟁을 보존하는 해결책. 데모 포함.

여기 내 해결책이 있습니다. 매우 유연하며 다른 사람들과 달리 외부 패키지를 요구해서는 안되며 남은 논쟁을 깨끗하게 처리해야합니다.

사용법은 다음과 같습니다. ./myscript -flag flagvariable -otherflag flagvar2

ValidFlags 라인을 편집하기 만하면됩니다. 하이픈을 준비하고 모든 인수를 검색합니다. 그런 다음 다음 인수를 깃발 이름으로 정의합니다.

./myscript -flag flagvariable -otherflag flagvar2
echo $flag $otherflag
flagvariable flagvar2

기본 코드 (짧은 버전, 예제가있는 장점, 오류가 발생한 버전) :

#!/usr/bin/env bash
#shebang.io
validflags="rate time number"
count=1
for arg in $@
do
    match=0
    argval=$1
    for flag in $validflags
    do
        sflag="-"$flag
        if [ "$argval" == "$sflag" ]
        then
            declare $flag=$2
            match=1
        fi
    done
        if [ "$match" == "1" ]
    then
        shift 2
    else
        leftovers=$(echo $leftovers $argval)
        shift
    fi
    count=$(($count+1))
done
#Cleanup then restore the leftovers
shift $#
set -- $leftovers

에코 데모에 내장 된 장점 버전 :

#!/usr/bin/env bash
#shebang.io
rate=30
time=30
number=30
echo "all args
$@"
validflags="rate time number"
count=1
for arg in $@
do
    match=0
    argval=$1
#   argval=$(echo $@ | cut -d ' ' -f$count)
    for flag in $validflags
    do
            sflag="-"$flag
        if [ "$argval" == "$sflag" ]
        then
            declare $flag=$2
            match=1
        fi
    done
        if [ "$match" == "1" ]
    then
        shift 2
    else
        leftovers=$(echo $leftovers $argval)
        shift
    fi
    count=$(($count+1))
done

#Cleanup then restore the leftovers
echo "pre final clear args:
$@"
shift $#
echo "post final clear args:
$@"
set -- $leftovers
echo "all post set args:
$@"
echo arg1: $1 arg2: $2

echo leftovers: $leftovers
echo rate $rate time $time number $number

최종적으로,이 하나는 유효하지 않은 경사가 통과되면 오류가 발생합니다.

#!/usr/bin/env bash
#shebang.io
rate=30
time=30
number=30
validflags="rate time number"
count=1
for arg in $@
do
    argval=$1
    match=0
        if [ "${argval:0:1}" == "-" ]
    then
        for flag in $validflags
        do
                sflag="-"$flag
            if [ "$argval" == "$sflag" ]
            then
                declare $flag=$2
                match=1
            fi
        done
        if [ "$match" == "0" ]
        then
            echo "Bad argument: $argval"
            exit 1
        fi
        shift 2
    else
        leftovers=$(echo $leftovers $argval)
        shift
    fi
    count=$(($count+1))
done
#Cleanup then restore the leftovers
shift $#
set -- $leftovers
echo rate $rate time $time number $number
echo leftovers: $leftovers

장점 : 그것이하는 일, 그것은 아주 잘 처리합니다. 여기에는 다른 많은 솔루션이없는 사용되지 않는 주장을 보존합니다. 또한 스크립트에서 손으로 정의하지 않고 변수를 호출 할 수 있습니다. 또한 해당 인수가 주어지지 않으면 변수를 미리 채웠습니다. (Verbose 예를 참조하십시오).

단점 : 단일 복잡한 arg 문자열을 구문 분석 할 수 없습니다. 예를 들어 -xcvf는 단일 인수로 처리합니다. 하지만이 기능을 추가하는 추가 코드를 더 쉽게 쓸 수 있습니다.

이름이 지정된 쉘 스크립트를 생성한다고 가정합니다 test_args.sh 다음과 같이

#!/bin/sh
until [ $# -eq 0 ]
do
  name=${1:1}; shift;
  if [[ -z "$1" || $1 == -* ]] ; then eval "export $name=true"; else eval "export $name=$1"; shift; fi  
done
echo "year=$year month=$month day=$day flag=$flag"

다음 명령을 실행 한 후 :

sh test_args.sh  -year 2017 -flag  -month 12 -day 22 

출력은 다음과 같습니다.

year=2017 month=12 day=22 flag=true

내 프로젝트를 제출하고 싶어요 : https://github.com/flyingangel/argparser

source argparser.sh
parse_args "$@"

그렇게 간단합니다. 환경은 인수와 동일한 이름의 변수로 채워집니다.

위치 및 깃발 기반 인수 혼합

--param = arg (Decals Dilimited)

위치 인수 사이의 깃발을 자유롭게 혼합 :

./script.sh dumbo 127.0.0.1 --environment=production -q -d
./script.sh dumbo --environment=production 127.0.0.1 --quiet -d

상당히 간결한 접근 방식으로 달성 할 수 있습니다.

# process flags
pointer=1
while [[ $pointer -le $# ]]; do
   param=${!pointer}
   if [[ $param != "-"* ]]; then ((pointer++)) # not a parameter flag so advance pointer
   else
      case $param in
         # paramter-flags with arguments
         -e=*|--environment=*) environment="${param#*=}";;
                  --another=*) another="${param#*=}";;

         # binary flags
         -q|--quiet) quiet=true;;
                 -d) debug=true;;
      esac

      # splice out pointer frame from positional list
      [[ $pointer -gt 1 ]] \
         && set -- ${@:1:((pointer - 1))} ${@:((pointer + 1)):$#} \
         || set -- ${@:((pointer + 1)):$#};
   fi
done

# positional remain
node_name=$1
ip_address=$2

-Param Arg (공간 구분)

혼합하지 않는 것이 일반적입니다 --flag=value 그리고 --flag value 스타일.

./script.sh dumbo 127.0.0.1 --environment production -q -d

이것은 읽을 수있는 약간의 멍청이이지만 여전히 유효합니다.

./script.sh dumbo --environment production 127.0.0.1 --quiet -d

원천

# process flags
pointer=1
while [[ $pointer -le $# ]]; do
   if [[ ${!pointer} != "-"* ]]; then ((pointer++)) # not a parameter flag so advance pointer
   else
      param=${!pointer}
      ((pointer_plus = pointer + 1))
      slice_len=1

      case $param in
         # paramter-flags with arguments
         -e|--environment) environment=${!pointer_plus}; ((slice_len++));;
                --another) another=${!pointer_plus}; ((slice_len++));;

         # binary flags
         -q|--quiet) quiet=true;;
                 -d) debug=true;;
      esac

      # splice out pointer frame from positional list
      [[ $pointer -gt 1 ]] \
         && set -- ${@:1:((pointer - 1))} ${@:((pointer + $slice_len)):$#} \
         || set -- ${@:((pointer + $slice_len)):$#};
   fi
done

# positional remain
node_name=$1
ip_address=$2

멋진 배쉬 도구를 작성하기 위해 배쉬 도우미를 썼습니다.

프로젝트 홈 : https://gitlab.mbedsys.org/mbedsys/bashopts

예시:

#!/bin/bash -ei

# load the library
. bashopts.sh

# Enable backtrace dusplay on error
trap 'bashopts_exit_handle' ERR

# Initialize the library
bashopts_setup -n "$0" -d "This is myapp tool description displayed on help message" -s "$HOME/.config/myapprc"

# Declare the options
bashopts_declare -n first_name -l first -o f -d "First name" -t string -i -s -r
bashopts_declare -n last_name -l last -o l -d "Last name" -t string -i -s -r
bashopts_declare -n display_name -l display-name -t string -d "Display name" -e "\$first_name \$last_name"
bashopts_declare -n age -l number -d "Age" -t number
bashopts_declare -n email_list -t string -m add -l email -d "Email adress"

# Parse arguments
bashopts_parse_args "$@"

# Process argument
bashopts_process_args

도움을 줄 것입니다 :

NAME:
    ./example.sh - This is myapp tool description displayed on help message

USAGE:
    [options and commands] [-- [extra args]]

OPTIONS:
    -h,--help                          Display this help
    -n,--non-interactive true          Non interactive mode - [$bashopts_non_interactive] (type:boolean, default:false)
    -f,--first "John"                  First name - [$first_name] (type:string, default:"")
    -l,--last "Smith"                  Last name - [$last_name] (type:string, default:"")
    --display-name "John Smith"        Display name - [$display_name] (type:string, default:"$first_name $last_name")
    --number 0                         Age - [$age] (type:number, default:0)
    --email                            Email adress - [$email_list] (type:string, default:"")

즐겨 :)

내 접근 방식은 다음과 같습니다. Regexp 사용.

  • getopts 없음
  • 짧은 매개 변수의 블록을 처리합니다 -qwerty
  • 짧은 매개 변수를 처리합니다 -q -w -e
  • 긴 옵션을 처리합니다 --qwerty
  • 짧거나 긴 옵션으로 속성을 전달할 수 있습니다 (짧은 옵션 블록을 사용하는 경우 속성이 마지막 옵션에 첨부됩니다).
  • 공간을 사용할 수 있습니다 = 속성을 제공하지만 속성은 하이픈+공간 "Delimiter"가 발생할 때까지 일치합니다. --q=qwe ty qwe ty 하나의 속성입니다
  • 위의 모든 것의 혼합을 처리합니다 -o a -op attr ibute --option=att ribu te --op-tion attribute --option att-ribute 유효합니다

스크립트:

#!/usr/bin/env sh

help_menu() {
  echo "Usage:

  ${0##*/} [-h][-l FILENAME][-d]

Options:

  -h, --help
    display this help and exit

  -l, --logfile=FILENAME
    filename

  -d, --debug
    enable debug
  "
}

parse_options() {
  case $opt in
    h|help)
      help_menu
      exit
     ;;
    l|logfile)
      logfile=${attr}
      ;;
    d|debug)
      debug=true
      ;;
    *)
      echo "Unknown option: ${opt}\nRun ${0##*/} -h for help.">&2
      exit 1
  esac
}
options=$@

until [ "$options" = "" ]; do
  if [[ $options =~ (^ *(--([a-zA-Z0-9-]+)|-([a-zA-Z0-9-]+))(( |=)(([\_\.\?\/\\a-zA-Z0-9]?[ -]?[\_\.\?a-zA-Z0-9]+)+))?(.*)|(.+)) ]]; then
    if [[ ${BASH_REMATCH[3]} ]]; then # for --option[=][attribute] or --option[=][attribute]
      opt=${BASH_REMATCH[3]}
      attr=${BASH_REMATCH[7]}
      options=${BASH_REMATCH[9]}
    elif [[ ${BASH_REMATCH[4]} ]]; then # for block options -qwert[=][attribute] or single short option -a[=][attribute]
      pile=${BASH_REMATCH[4]}
      while (( ${#pile} > 1 )); do
        opt=${pile:0:1}
        attr=""
        pile=${pile/${pile:0:1}/}
        parse_options
      done
      opt=$pile
      attr=${BASH_REMATCH[7]}
      options=${BASH_REMATCH[9]}
    else # leftovers that don't match
      opt=${BASH_REMATCH[10]}
      options=""
    fi
    parse_options
  fi
done

이 예에서는 사용 방법을 보여줍니다. getopt 그리고 eval 그리고 HEREDOC 그리고 shift 다음에 나오는 필수 값이 있거나 없는 짧고 긴 매개변수를 처리합니다.또한 switch/case 문은 간결하고 따라하기 쉽습니다.

#!/usr/bin/env bash

# usage function
function usage()
{
   cat << HEREDOC

   Usage: $progname [--num NUM] [--time TIME_STR] [--verbose] [--dry-run]

   optional arguments:
     -h, --help           show this help message and exit
     -n, --num NUM        pass in a number
     -t, --time TIME_STR  pass in a time string
     -v, --verbose        increase the verbosity of the bash script
     --dry-run            do a dry run, dont change any files

HEREDOC
}  

# initialize variables
progname=$(basename $0)
verbose=0
dryrun=0
num_str=
time_str=

# use getopt and store the output into $OPTS
# note the use of -o for the short options, --long for the long name options
# and a : for any option that takes a parameter
OPTS=$(getopt -o "hn:t:v" --long "help,num:,time:,verbose,dry-run" -n "$progname" -- "$@")
if [ $? != 0 ] ; then echo "Error in command line arguments." >&2 ; usage; exit 1 ; fi
eval set -- "$OPTS"

while true; do
  # uncomment the next line to see how shift is working
  # echo "\$1:\"$1\" \$2:\"$2\""
  case "$1" in
    -h | --help ) usage; exit; ;;
    -n | --num ) num_str="$2"; shift 2 ;;
    -t | --time ) time_str="$2"; shift 2 ;;
    --dry-run ) dryrun=1; shift ;;
    -v | --verbose ) verbose=$((verbose + 1)); shift ;;
    -- ) shift; break ;;
    * ) break ;;
  esac
done

if (( $verbose > 0 )); then

   # print out all the parameters we read in
   cat <<-EOM
   num=$num_str
   time=$time_str
   verbose=$verbose
   dryrun=$dryrun
EOM
fi

# The rest of your script below

위 스크립트의 가장 중요한 줄은 다음과 같습니다.

OPTS=$(getopt -o "hn:t:v" --long "help,num:,time:,verbose,dry-run" -n "$progname" -- "$@")
if [ $? != 0 ] ; then echo "Error in command line arguments." >&2 ; exit 1 ; fi
eval set -- "$OPTS"

while true; do
  case "$1" in
    -h | --help ) usage; exit; ;;
    -n | --num ) num_str="$2"; shift 2 ;;
    -t | --time ) time_str="$2"; shift 2 ;;
    --dry-run ) dryrun=1; shift ;;
    -v | --verbose ) verbose=$((verbose + 1)); shift ;;
    -- ) shift; break ;;
    * ) break ;;
  esac
done

짧고, 간단하고, 읽기 쉽고, 거의 모든 것을 처리합니다(IMHO).

누군가에게 도움이 되기를 바랍니다.

모듈 "인수"를 사용하십시오 배쉬 모듈

예시:

#!/bin/bash
. import.sh log arguments

NAME="world"

parse_arguments "-n|--name)NAME;S" -- "$@" || {
  error "Cannot parse command line."
  exit 1
}

info "Hello, $NAME!"

이것은 또한 값을 설정할 수 있고 누군가가 입력을 제공하면 해당 값으로 기본값을 재정의 할 수 있습니다.

myscript.sh -f ./serverlist.txt 또는 just ./myscript.sh (및 기본값이 필요합니다)

    #!/bin/bash
    # --- set the value, if there is inputs, override the defaults.

    HOME_FOLDER="${HOME}/owned_id_checker"
    SERVER_FILE_LIST="${HOME_FOLDER}/server_list.txt"

    while [[ $# > 1 ]]
    do
    key="$1"
    shift

    case $key in
        -i|--inputlist)
        SERVER_FILE_LIST="$1"
        shift
        ;;
    esac
    done


    echo "SERVER LIST   = ${SERVER_FILE_LIST}"

getOpt [s], posix, Old Unix 스타일이없는 다른 솔루션

비슷하다 솔루션 Bruno Bronosky가 게시되었습니다 여기에는 사용이없는 것이 있습니다 getopt(s).

내 솔루션의 주요 차별화 기능은 tar -xzf foo.tar.gz 와 동등하다 tar -x -z -f foo.tar.gz. 그리고 그냥처럼 tar, ps 선행 하이픈은 짧은 옵션 블록을위한 선택 사항입니다 (그러나 이것은 쉽게 변경할 수 있습니다). 긴 옵션도 지원됩니다 (그러나 블록이 1 개로 시작하면 두 개의 주요 하이픈이 필요합니다).

예제 옵션이 포함 된 코드

#!/bin/sh

echo
echo "POSIX-compliant getopt(s)-free old-style-supporting option parser from phk@[se.unix]"
echo

print_usage() {
  echo "Usage:

  $0 {a|b|c} [ARG...]

Options:

  --aaa-0-args
  -a
    Option without arguments.

  --bbb-1-args ARG
  -b ARG
    Option with one argument.

  --ccc-2-args ARG1 ARG2
  -c ARG1 ARG2
    Option with two arguments.

" >&2
}

if [ $# -le 0 ]; then
  print_usage
  exit 1
fi

opt=
while :; do

  if [ $# -le 0 ]; then

    # no parameters remaining -> end option parsing
    break

  elif [ ! "$opt" ]; then

    # we are at the beginning of a fresh block
    # remove optional leading hyphen and strip trailing whitespaces
    opt=$(echo "$1" | sed 's/^-\?\([a-zA-Z0-9\?-]*\)/\1/')

  fi

  # get the first character -> check whether long option
  first_chr=$(echo "$opt" | awk '{print substr($1, 1, 1)}')
  [ "$first_chr" = - ] && long_option=T || long_option=F

  # note to write the options here with a leading hyphen less
  # also do not forget to end short options with a star
  case $opt in

    -)

      # end of options
      shift
      break
      ;;

    a*|-aaa-0-args)

      echo "Option AAA activated!"
      ;;

    b*|-bbb-1-args)

      if [ "$2" ]; then
        echo "Option BBB with argument '$2' activated!"
        shift
      else
        echo "BBB parameters incomplete!" >&2
        print_usage
        exit 1
      fi
      ;;

    c*|-ccc-2-args)

      if [ "$2" ] && [ "$3" ]; then
        echo "Option CCC with arguments '$2' and '$3' activated!"
        shift 2
      else
        echo "CCC parameters incomplete!" >&2
        print_usage
        exit 1
      fi
      ;;

    h*|\?*|-help)

      print_usage
      exit 0
      ;;

    *)

      if [ "$long_option" = T ]; then
        opt=$(echo "$opt" | awk '{print substr($1, 2)}')
      else
        opt=$first_chr
      fi
      printf 'Error: Unknown option: "%s"\n' "$opt" >&2
      print_usage
      exit 1
      ;;

  esac

  if [ "$long_option" = T ]; then

    # if we had a long option then we are going to get a new block next
    shift
    opt=

  else

    # if we had a short option then just move to the next character
    opt=$(echo "$opt" | awk '{print substr($1, 2)}')

    # if block is now empty then shift to the next one
    [ "$opt" ] || shift

  fi

done

echo "Doing something..."

exit 0

예제 사용은 아래 예제를 참조하십시오.

인수가있는 옵션의 위치

그 가치가있는 것에 대해 논쟁의 옵션은 마지막이 아닙니다 (긴 옵션 만 필요합니다). 그래서 예를 들어 tar (적어도 일부 구현에서) f 파일 이름이 다음에 옵션이어야합니다 (tar xzf bar.tar.gz 작동하지만 tar xfz bar.tar.gz 그렇지 않습니다) 이것은 여기에 그렇지 않습니다 (이후 예 참조).

인수가있는 여러 옵션

또 다른 보너스로서 옵션 매개 변수는 필요한 옵션이있는 매개 변수에 의해 옵션 순서대로 소비됩니다. 명령 줄이있는 내 스크립트의 출력 만보세요 abc X Y Z (또는 -abc X Y Z):

Option AAA activated!
Option BBB with argument 'X' activated!
Option CCC with arguments 'Y' and 'Z' activated!

긴 옵션도 연결되었습니다

또한 블록에서 마지막으로 발생한다는 점을 감안할 때 옵션 블록에 긴 옵션을 가질 수도 있습니다. 따라서 다음 명령 줄은 모두 동일합니다 (옵션 및 인수가 처리되는 순서 포함).

  • -cba Z Y X
  • cba Z Y X
  • -cb-aaa-0-args Z Y X
  • -c-bbb-1-args Z Y X -a
  • --ccc-2-args Z Y -ba X
  • c Z Y b X a
  • -c Z Y -b X -a
  • --ccc-2-args Z Y --bbb-1-args X --aaa-0-args

이 모든 것이 다음으로 이어집니다.

Option CCC with arguments 'Z' and 'Y' activated!
Option BBB with argument 'X' activated!
Option AAA activated!
Doing something...

이 솔루션에는 없습니다

선택적 인수

하이픈이없는 블록이 있는지 기대함으로써 약간의 작업으로 선택적인 인수가있는 옵션이 가능해야합니다. 그런 다음 사용자는 옵션 매개 변수가있는 매개 변수가있는 블록을 따라 모든 블록 앞에 하이픈을 넣어야합니다. 어쩌면 이것은 사용자에게 의사 소통하기에는 너무 복잡 할 수 있으므로이 경우에는 선행 하이픈이 필요합니다.

여러 가능한 매개 변수로 인해 상황이 훨씬 더 복잡해집니다. 나는 인수가 그에 대한 주장인지 아닌지를 결정함으로써 옵션을 똑똑하게 만들려고하는 것에 대해 조언 할 것입니다 (예 : 옵션이있는 것은 선택적 인수로 숫자를 취합니다).

나는 선택적인 인수 대신 개인적으로 추가 옵션을 선호합니다.

동일한 부호로 도입 된 옵션 인수

선택적인 인수와 마찬가지로 나는 이것의 팬이 아닙니다 (BTW, 다른 매개 변수 스타일의 장단점을 논의 할 스레드가 있습니까?) 그러나 원한다면 이것을 원한다면 아마도 직접 구현할 수 있습니다. http://mywiki.wooledge.org/bashfaq/035#manual_loop a --long-with-arg=?* 사례 진술서와 동등한 부호를 벗기는 (이것은 BTW입니다.이 사이트는 약간의 노력으로 매개 변수를 연결하는 것이 가능하지만 "독자를위한 연습으로 남겨 두는 것"이라고 말하지만, 나는 그들의 말로 그들을 데려 갔지만 나는 시작했지만 나는 시작했습니다. 할퀴다).

다른 메모

Posix Compliant, 내가 다루어야 할 고대 바쁜 박스 설정에서도 작품 cut, head 그리고 getopts 잃어버린).

이 질문에 대한 최고 답변은 내가 시도했을 때 약간 버그가있는 것처럼 보였습니다. 여기에 더 강력한 솔루션이 있습니다.

boolean_arg=""
arg_with_value=""

while [[ $# -gt 0 ]]
do
key="$1"
case $key in
    -b|--boolean-arg)
    boolean_arg=true
    shift
    ;;
    -a|--arg-with-value)
    arg_with_value="$2"
    shift
    shift
    ;;
    -*)
    echo "Unknown option: $1"
    exit 1
    ;;
    *)
    arg_num=$(( $arg_num + 1 ))
    case $arg_num in
        1)
        first_normal_arg="$1"
        shift
        ;;
        2)
        second_normal_arg="$1"
        shift
        ;;
        *)
        bad_args=TRUE
    esac
    ;;
esac
done

# Handy to have this here when adding arguments to
# see if they're working. Just edit the '0' to be '1'.
if [[ 0 == 1 ]]; then
    echo "first_normal_arg: $first_normal_arg"
    echo "second_normal_arg: $second_normal_arg"
    echo "boolean_arg: $boolean_arg"
    echo "arg_with_value: $arg_with_value"
    exit 0
fi

if [[ $bad_args == TRUE || $arg_num < 2 ]]; then
    echo "Usage: $(basename "$0") <first-normal-arg> <second-normal-arg> [--boolean-arg] [--arg-with-value VALUE]"
    exit 1
fi

다음은 가변 배열을 사용한 Bruno Bronosky의 답변에 대한 개선 된 솔루션입니다.

매개 변수 위치를 혼합하고 옵션없이 순서를 유지하는 매개 변수 배열을 제공 할 수 있습니다.

#!/bin/bash

echo $@

PARAMS=()
SOFT=0
SKIP=()
for i in "$@"
do
case $i in
    -n=*|--skip=*)
    SKIP+=("${i#*=}")
    ;;
    -s|--soft)
    SOFT=1
    ;;
    *)
        # unknown option
        PARAMS+=("$i")
    ;;
esac
done
echo "SKIP            = ${SKIP[@]}"
echo "SOFT            = $SOFT"
    echo "Parameters:"
    echo ${PARAMS[@]}

예를 들어 출력됩니다.

$ ./test.sh parameter -s somefile --skip=.c --skip=.obj
parameter -s somefile --skip=.c --skip=.obj
SKIP            = .c .obj
SOFT            = 1
Parameters:
parameter somefile

간단하고 쉽게 수정하기 쉬운 매개 변수는 순서대로있을 수 있습니다. 이것은 모든 형태 (-a, -a, a 등)로 매개 변수를 가져 오도록 수정할 수 있습니다.

for arg in "$@"
do
   key=$(echo $arg | cut -f1 -d=)`
   value=$(echo $arg | cut -f2 -d=)`
   case "$key" in
        name|-name)      read_name=$value;;
        id|-id)          read_id=$value;;
        *)               echo "I dont know what to do with this"
   ease
done
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top