programing

getopts를 사용하여 긴 명령줄 및 짧은 명령줄 옵션 처리

bestprogram 2023. 5. 27. 12:02

getopts를 사용하여 긴 명령줄 및 짧은 명령줄 옵션 처리

셸 스크립트를 사용하여 길고 짧은 형태의 명령줄 옵션을 호출하고 싶습니다.

나는 그것을 알고 있습니다.getopts사용할 수 있지만 Perl에서와 마찬가지로 셸에서 동일한 작업을 수행할 수 없습니다.

다음과 같은 옵션을 사용할 수 있도록 이 작업을 수행하는 방법에 대한 모든 아이디어:

./shell.sh --copyfile abc.pl /tmp/
./shell.sh -c abc.pl /tmp/

셸에 이지만, 위서두명는내셸에동일의한가미다다사지니용합음을 합니다.getopts제가 이것들을 실행할 수 없습니까?

getopt그리고.getopts다른 짐승들이고, 사람들은 그들이 하는 일에 대해 약간의 오해를 가지고 있는 것 같습니다. getopts는 에대기제명다니에 대한 입니다.bash루프에서 명령줄 옵션을 처리하고 찾은 각 옵션과 값을 기본 제공 변수에 차례로 할당하여 추가로 처리할 수 있도록 합니다. getopt그러나 외부 유틸리티 프로그램이며 실제로 bash와 같은 방식으로 옵션을 처리하지 않습니다.getoptsGetopt Python 모듈이썬optparse/argparse모듈이 합니다. 모든 것.getopt는 전달된 옵션을 표준화합니다. 즉, 셸 스크립트가 보다 쉽게 처리할 수 있도록 보다 표준적인 형식으로 변환합니다.를 들어, 예를들, 어프그램의 은 다음과 같습니다.getopt다음을 변환할 수 있습니다.

myscript -ab infile.txt -ooutfile.txt

여기에:

myscript -a -b -o outfile.txt infile.txt

당신은 실제 처리를 직접 해야 합니다.은 필요없다니습가할을 사용할 필요가 없습니다.getopt옵션을 지정할 수 있는 방법을 다양하게 제한하는 경우:

  • 인수당 하나의 옵션만 입력합니다.
  • 모든 옵션은 위치 매개변수(즉, 비옵션 인수) 앞에 놓입니다.
  • 의 : 있옵의예경우션는값이예(▁with경▁for-o위), 값은 별도의 인수(공백 뒤)로 지정해야 합니다.

을 사용하는 getoptgetopts기본적인 이유는 GNU만이기 때문입니다.getopt이름이 긴 명령줄 1옵션을 지원합니다.(GNU)getopt입니다.X 및 유용하지 않은 Mac OS X » FreeB와 됩니다.SD는 기본적으로 제공되지만 그다지 유용하지는 않습니다.getopt그러나 GNU 버전은 설치할 수 있습니다. 아래를 참조하십시오.)

예를 들어, 여기 GNU를 사용하는 예가 있습니다.getopt라고 불리는 나의 대본에서.javawrap:

# NOTE: This requires GNU getopt.  On Mac OS X and FreeBSD, you have to install this
# separately; see below.
TEMP=$(getopt -o vdm: --long verbose,debug,memory:,debugfile:,minheap:,maxheap: \
              -n 'javawrap' -- "$@")

if [ $? != 0 ] ; then echo "Terminating..." >&2 ; exit 1 ; fi

# Note the quotes around '$TEMP': they are essential!
eval set -- "$TEMP"

VERBOSE=false
DEBUG=false
MEMORY=
DEBUGFILE=
JAVA_MISC_OPT=
while true; do
  case "$1" in
    -v | --verbose ) VERBOSE=true; shift ;;
    -d | --debug ) DEBUG=true; shift ;;
    -m | --memory ) MEMORY="$2"; shift 2 ;;
    --debugfile ) DEBUGFILE="$2"; shift 2 ;;
    --minheap )
      JAVA_MISC_OPT="$JAVA_MISC_OPT -XX:MinHeapFreeRatio=$2"; shift 2 ;;
    --maxheap )
      JAVA_MISC_OPT="$JAVA_MISC_OPT -XX:MaxHeapFreeRatio=$2"; shift 2 ;;
    -- ) shift; break ;;
    * ) break ;;
  esac
done

이를 통해 다음과 같은 옵션을 지정할 수 있습니다.--verbose -dm4096 --minh=20 --maxhe 40 --debugfi="/Users/John Johnson/debug.txt"또는 그와 유사합니다.통화의 효과가 다음과 같습니다.getopt을 옵을표는것다니입하화준션ize로 입니다.--verbose -d -m 4096 --minheap 20 --maxheap 40 --debugfile "/Users/John Johnson/debug.txt"더 쉽게 처리할 수 있습니다. 인문용한▁around문▁quoting▁the인에"$1"그리고."$2"공백이 있는 인수를 올바르게 처리하는 것이 중요합니다.

처음 9줄을 삭제하는 경우(을(를) 통해 모든 항목)eval setline), 코드는 여전히 작동합니다!그러나 코드가 허용하는 옵션의 종류는 훨씬 더 까다로울 것입니다.특히 위에서 설명한 "규례적" 형식으로 모든 옵션을 지정해야 합니다.를 사용하여getopt옵션을 하거나, 더 짧은 의 긴 문자를 하거나, , 션다자수형긴있사문습니할용태의를자자비짧문은ambiguous러를 할 수 . 다음 중 하나를 사용할 수 있습니다.--file foo.txt또는--file=foo.txt둘중하, 나를사용일을 합니다.-m 4096또는-m4096스타일, 혼합 옵션 및 임의의 순서로 정렬되지 않은 옵션 등. getopt또한 인식할 수 없거나 모호한 옵션이 발견되면 오류 메시지를 출력합니다.

참고: 실제로 완전히 다른가지 버전이 있습니다.getopt인, 기본적인getopt GNU » GNUgetopt기능과 호출 규약이 2다릅니다.기본의getopt꽤 부서졌습니다: 긴 옵션을 처리하지 않을 뿐만 아니라 인수 또는 빈 인수 내부의 내장된 공간도 처리할 수 없습니다. 반면getopts이 일을 제대로 합니다.는 기본적인 에서 입니다.getopt GNUgetopt는 Linux에 기본적으로 설치되지만 Mac OS X 및 FreeBSD에는 별도로 설치해야 합니다.Mac OS X에서 MacPorts(http://www.macports.org )를 설치한 다음sudo port install getopt GNU를 getopt (으)로)/opt/local/bin), ), 을/opt/local/bin앞에 있는 당신의 껍데기 길에 있습니다./usr/bin 설치SD에서 설치misc/getopt.

사용 중인 프로그램의 예제 코드를 수정하기 위한 빠른 안내서:처음 몇 개의 회선 중 호출하는 회선을 제외하고 모두 동일하게 유지되어야 하는 "보일러 플레이트"입니다.getopt프로그램 이름을 변경해야 합니다.-n 에단옵지정 에 short -o그리고 긴 옵션은 다음과 같습니다.--long값을 사용하는 옵션 뒤에 콜론을 배치합니다.

마지막으로, 만약 당신이 방금 전의 코드를 본다면.seteval set그것은 BSD를 위해 쓰여졌습니다.getopt해야 합니다.eval set 잘 스타일getoptset GNU와 getopt.

1사실은.getoptsksh93긴 이름의 옵션을 지원하지만 이 셸은 자주 사용되지 않습니다.bashzsh,사용하다zparseopts이 기능을 사용할 수 있습니다.

2엄밀히 말하면, "GNU는getopt는 잘못된 이름입니다. 이 버전은 실제로 GNU 프로젝트가 아닌 Linux용으로 작성되었습니다. 규약과 "GNU 를러용모나다따릅니어든"라는.getopt일반적으로 사용됩니다(예: FreeBSD).

세 가지 구현을 고려할 수 있습니다.

  • 기능getopts이중 대시 접두사를 사용하는 긴 옵션 이름은 지원하지 않습니다.단일 문자 옵션만 지원합니다.

  • BSD UNIX의 BSD UNIX getopt명령(MacOS에서 사용하는 명령)입니다.긴 옵션도 지원하지 않습니다.

  • 실행형 GNU의 GNU getoptGNU(Linux의 명령줄에서 사용)는 긴 옵션 구문 분석을 지원합니다.


일부 다른 답변은 내장된 Bash를 사용하기 위한 솔루션을 보여줍니다.getopts긴 옵션을 모방합니다. 문자가 인 짧은 .그래서 당신은 "--"을 깃발로 받습니다.그 모든 , 된 OPTARG로 는 OPTARG는 OPTARG입니다.case.

이는 현명하지만 다음과 같은 주의 사항이 수반됩니다.

  • getopts옵션 사양을 적용할 수 없습니다.사용자가 잘못된 옵션을 제공하면 오류를 반환할 수 없습니다.당신은 OPTARG를 구문 분석할 때 직접 오류 검사를 해야 합니다.
  • OPTARG는 긴 옵션 이름에 사용되며, 긴 옵션 자체에 인수가 있을 경우 사용이 복잡해집니다.추가 사례로 코딩해야 합니다.

따라서 긴 옵션에 대한 지원 부족을 해결하기 위해 더 많은 코드를 작성할 수 있지만, 이는 훨씬 더 많은 작업이며 코드를 단순화하기 위해 getopt 파서를 사용하는 목적을 부분적으로 무시합니다.

Bash 내장 getopts 함수는 대시 문자 다음에 콜론을 optspec에 넣어 긴 옵션을 구문 분석하는 데 사용할 수 있습니다.

#!/usr/bin/env bash 
optspec=":hv-:"
while getopts "$optspec" optchar; do
    case "${optchar}" in
        -)
            case "${OPTARG}" in
                loglevel)
                    val="${!OPTIND}"; OPTIND=$(( $OPTIND + 1 ))
                    echo "Parsing option: '--${OPTARG}', value: '${val}'" >&2;
                    ;;
                loglevel=*)
                    val=${OPTARG#*=}
                    opt=${OPTARG%=$val}
                    echo "Parsing option: '--${opt}', value: '${val}'" >&2
                    ;;
                *)
                    if [ "$OPTERR" = 1 ] && [ "${optspec:0:1}" != ":" ]; then
                        echo "Unknown option --${OPTARG}" >&2
                    fi
                    ;;
            esac;;
        h)
            echo "usage: $0 [-v] [--loglevel[=]<value>]" >&2
            exit 2
            ;;
        v)
            echo "Parsing option: '-${optchar}'" >&2
            ;;
        *)
            if [ "$OPTERR" != 1 ] || [ "${optspec:0:1}" = ":" ]; then
                echo "Non-option argument: '-${OPTARG}'" >&2
            fi
            ;;
    esac
done

이름으로 한 후 에행파실 = 복한후사getopts_test.sh현재 작업 디렉토리에서 다음과 같은 출력을 생성할 수 있습니다.

$ ./getopts_test.sh
$ ./getopts_test.sh -f
Non-option argument: '-f'
$ ./getopts_test.sh -h
usage: code/getopts_test.sh [-v] [--loglevel[=]<value>]
$ ./getopts_test.sh --help
$ ./getopts_test.sh -v
Parsing option: '-v'
$ ./getopts_test.sh --very-bad
$ ./getopts_test.sh --loglevel
Parsing option: '--loglevel', value: ''
$ ./getopts_test.sh --loglevel 11
Parsing option: '--loglevel', value: '11'
$ ./getopts_test.sh --loglevel=11
Parsing option: '--loglevel', value: '11'

분명히 getopts는 둘 다 수행하지 않습니다.OPTERR긴 옵션에 대해 옵션 구문 분석을 선택하거나 선택하지 않습니다.위의 스크립트 조각은 이 작업을 수동으로 수행하는 방법을 보여줍니다.기본 원리는 데비안 알퀴스트 셸("대시")에서도 작동합니다.특수한 경우에 유의하십시오.

getopts -- "-:"  ## without the option terminator "-- " bash complains about "-:"
getopts "-:"     ## this works in the Debian Almquist shell ("dash")

http://mywiki.wooledge.org/BashFAQ 의 GreyCat이 지적하듯이, 이 트릭은 셸의 비표준적인 동작을 이용하여 "-ffilename"에서와 같이 옵션-filename(즉, "-filename"의 파일 이름)을 옵션에 연결할 수 있습니다.POSIX 표준은 "--long option"의 경우 옵션 구문 분석을 종료하고 모든 긴 옵션을 비옵션 인수로 전환하는 공백이 있어야 한다고 말합니다.

제공되는 트인getoptsAFAIK 명령은 여전히 단일 문자 옵션으로만 제한됩니다.

프로그램이.getopt분석하기 쉽도록 옵션 집합을 재구성할 수 있습니다.긴 옵션을 처리할 수 있도록 설계를 조정할 수도 있습니다.사용 예:

aflag=no
bflag=no
flist=""
set -- $(getopt abf: "$@")
while [ $# -gt 0 ]
do
    case "$1" in
    (-a) aflag=yes;;
    (-b) bflag=yes;;
    (-f) flist="$flist $2"; shift;;
    (--) shift; break;;
    (-*) echo "$0: error - unrecognized option $1" 1>&2; exit 1;;
    (*)  break;;
    esac
    shift
done

# Process remaining non-option arguments
...

으로 유한체사수있다습니용할과 함께 할 수 .getoptlong지휘권

부의적근약점은의 하세요.getopt프로그램은 공간이 있는 인수를 처리하고 해당 공간을 정확하게 보존하는 데 어려움이 있습니다.이것이 내장된 이유입니다.getopts단일 문자 옵션만 처리하기 때문에 제한적이긴 하지만 더 우수합니다.

긴옵은표구분수있석다습니할문으로 구문 할 수 .getopts에 대한 "인스턴트"로 기본 제공됩니다.-", "선택사항"

이것은 휴대가 가능한 네이티브 POSIX 셸이며 외부 프로그램이나 바시즘이 필요하지 않습니다.

이 에서는 이안서긴다같구인은다니현합에 로 긴 합니다.- so 션옵, 그서래--alpha에 의해 보이는getopts~하듯이- 의으로로alpha그리고.--bravo=foo 보여집니다.- 의으로로bravo=foo실제 인수는 셸 매개 변수 확장, 업데이트를 통해 수집됩니다.$OPT그리고.$OPTARG.

예에서는, 이예서는에,는,-b그리고.-c의 긴 형태, (그리고그긴형태의들형,태그,긴▁(▁()--bravo그리고.--charlie 인수가 .에는 필수 인수가 있습니다.에 대한 뒤에 를 들어 긴 옵 에 는 등 뒤 에 옵 니 다 호 인 수 션 대 한 다--bravo=foo(긴 옵션에 대한 공백 구분 기호는 구현하기 어려울 수 있습니다(아래 참조).

이 솔루션은 내장된 기능을 사용하므로 다음과 같은 용도를 지원합니다.cmd --bravo=foo -ac FILE된 옵션이 )-a그리고.-c그리고 표준 옵션과 함께 긴 옵션을 인터리브합니다.) 다른 대부분의 답변은 이를 수행하는 데 어려움을 겪거나 실패합니다.

die() { echo "$*" >&2; exit 2; }  # complain to STDERR and exit with error
needs_arg() { if [ -z "$OPTARG" ]; then die "No arg for --$OPT option"; fi; }

while getopts ab:c:-: OPT; do
  # support long options: https://stackoverflow.com/a/28466267/519360
  if [ "$OPT" = "-" ]; then   # long option: reformulate OPT and OPTARG
    OPT="${OPTARG%%=*}"       # extract long option name
    OPTARG="${OPTARG#$OPT}"   # extract long option argument (may be empty)
    OPTARG="${OPTARG#=}"      # if long option argument, remove assigning `=`
  fi
  case "$OPT" in
    a | alpha )    alpha=true ;;
    b | bravo )    needs_arg; bravo="$OPTARG" ;;
    c | charlie )  needs_arg; charlie="$OPTARG" ;;
    ??* )          die "Illegal option --$OPT" ;;  # bad long option
    ? )            exit 2 ;;  # bad short option (error reported via getopts)
  esac
done
shift $((OPTIND-1)) # remove parsed options and args from $@ list

일 때 (옵션이대때일시때(▁the▁when옵)-긴입니다.), 긴옵 다니입션다▁),. getopts long 을 실제 롱옵 에을구 분것을입니로 구문 했을 것입니다.$OPTARG를 들어, 예를 들어, 기호입니다.--bravo=foo는 원의세트로 설정합니다.OPT='-'그리고.OPTARG='bravo=foo'.if 세트 탠자트$OPT$OPTARG첫 번째 등호 앞에 (bravo을 제거한 합니다.$OPTARG)로 표시됩니다.=foo에서, "" " " " " " " 이 빈입니다.=마지막으로, 우리는 논쟁의 주도권을 박탈합니다.=시점에서, 시서에점이,,$OPT짧은 옵션(한 문자) 또는 긴 옵션(2자 이상)입니다.

case 짧은 파긴또옵는션옵프션이파옵(▁either,션▁(▁then긴,이프▁options▁pipe▁matchesthe▁long,|또는 "또는" 작동을 나타냅니다.다과 같장 전용 옵션음과 긴 전용 delta ) delta=true ;;파이프가 필요하지 않음).짧옵션의경우은,경우▁options짧,▁for▁short.getopts인수에 불평하기 에, 는 옵션과 누락된 인수를 사용하여 .needs_arg기능, 다음과 같은 경우 치명적으로 종료됩니다.$OPTARG비어 있습니다.??* 긴 옵션과 합니다(" 옵션).?단일 문자와 일치합니다.*하므로 0과 일치합니다.??*2자 이상 일치), 종료하기 전에 "불법 옵션" 오류를 발생시킬 수 있습니다.

의 긴 옵션은 으로 GNU를 제공합니다.--구문분중니다지합을석다,니▁will중▁parsing합.-a -- --bravo=4를 설정합니다.$alphatrue그렇지만$bravo손도 대지 않은 채로 남을 것이고,$1▁▁be 될 것입니다.--bravo=4파일 이름에 선행 대시를 사용하는 것이 좋다고는 말할 수 없지만, 이렇게 하면 파일 이름이 옵션이 아니라는 것을 알 수 있습니다.


사소한 버그: 누군가 잘못된 단일 문자 긴 옵션(단축 옵션도 아님)을 제공하면 오류와 함께 종료되지만 메시지는 표시되지 않습니다(이 구현에서는 짧은 옵션으로 가정함).당신은 그것을 추적할 수 있습니다 앞의 조건에 추가 변수.case그리고 최종 케이스 상태에서 테스트를 해보겠지만, 저는 그것이 귀찮기에는 너무 어려운 케이스라고 생각합니다.

대문자 변수 이름:일반적으로 시스템 사용을 위해 모든 대문자 변수를 예약하는 것이 좋습니다.나는 가지고 있습니다$OPT그것과 일치하도록 전력을 다해.$OPTARG하지만 이것은 그 관습을 깨뜨립니다.저는 이것이 시스템이 해야 할 일이고 안전해야 하기 때문에 적합하다고 생각합니다. 저는 이 변수 이름을 사용하는 표준에 대해 들어본 적이 없습니다.

긴 옵션에 대한 예상치 못한 인수에 대해 불평하기흉내내기needs_arg예상치 못한 상황에서 논쟁에 대해 불평하는 뒤집힌 테스트로:

no_arg() { if [ -n "$OPTARG" ]; then die "No arg allowed for --$OPT option"; fi; }

공백으로 구분된 인수가 있는 긴 옵션을 사용하는 방법당신은 다음 논쟁을 끌어들일 수 있습니다.eval "ARG_B=\"\$$OPTIND\""(또는 배쉬의 간접적인 확장으로)ARG_B="${!OPTIND}"후 ) 후분증분$OPTIND 답변의 이전 버전에서 언급한 바와 같이, 신뢰할 수 없습니다.getopts될 수 은 수동 $OPTIND.

다음은 긴 옵션과 함께 getopt를 실제로 사용하는 예입니다.

aflag=no
bflag=no
cargument=none

# options may be followed by one colon to indicate they have a required argument
if ! options=$(getopt -o abc: -l along,blong,clong: -- "$@")
then
    # something went wrong, getopt will put out an error message for us
    exit 1
fi

set -- $options

while [ $# -gt 0 ]
do
    case $1 in
    -a|--along) aflag="yes" ;;
    -b|--blong) bflag="yes" ;;
    # for options with required arguments, an additional shift is required
    -c|--clong) cargument="$2" ; shift;;
    (--) shift; break;;
    (-*) echo "$0: error - unrecognized option $1" 1>&2; exit 1;;
    (*) break;;
    esac
    shift
done

용사를 합니다.getoptsshort하여 /etc/인수를 합니다.


모든 조합과 함께 작동합니다. 예:

  • foo bar -f --bar
  • 푸바 --푸-b
  • foobar - bf --bar --foobar
  • foobar -fbFBA short --bar -FB --foo=longhorn
  • foobar -fA "text shorty" -B --text="text longhorn"
  • bash foo boo -F --barfoo
  • shofoobar -B --foobar - ...
  • bash ./foobar -F --bar

이 예에 대한 몇 가지 선언

Options=$@
Optnum=$#
sfoo='no '
sbar='no '
sfoobar='no '
sbarfoo='no '
sarguments='no '
sARG=empty
lfoo='no '
lbar='no '
lfoobar='no '
lbarfoo='no '
larguments='no '
lARG=empty

사용 기능의 모양

function _usage() 
{
  ###### U S A G E : Help and ERROR ######
  cat <<EOF
   foobar $Options
  $*
          Usage: foobar <[options]>
          Options:
                  -b   --bar            Set bar to yes    ($foo)
                  -f   --foo            Set foo to yes    ($bart)
                  -h   --help           Show this message
                  -A   --arguments=...  Set arguments to yes ($arguments) AND get ARGUMENT ($ARG)
                  -B   --barfoo         Set barfoo to yes ($barfoo)
                  -F   --foobar         Set foobar to yes ($foobar)
EOF
}

[ $# = 0 ] && _usage "  >>>>>>>> no options given "

getopslong와 long 인수를 사용하는 에는 " " "/"이라는 단어가 있습니다.

while getopts ':bfh-A:BF' OPTION ; do
  case "$OPTION" in
    b  ) sbar=yes                       ;;
    f  ) sfoo=yes                       ;;
    h  ) _usage                         ;;   
    A  ) sarguments=yes;sARG="$OPTARG"  ;;
    B  ) sbarfoo=yes                    ;;
    F  ) sfoobar=yes                    ;;
    -  ) [ $OPTIND -ge 1 ] && optind=$(expr $OPTIND - 1 ) || optind=$OPTIND
         eval OPTION="\$$optind"
         OPTARG=$(echo $OPTION | cut -d'=' -f2)
         OPTION=$(echo $OPTION | cut -d'=' -f1)
         case $OPTION in
             --foo       ) lfoo=yes                       ;;
             --bar       ) lbar=yes                       ;;
             --foobar    ) lfoobar=yes                    ;;
             --barfoo    ) lbarfoo=yes                    ;;
             --help      ) _usage                         ;;
             --arguments ) larguments=yes;lARG="$OPTARG"  ;; 
             * )  _usage " Long: >>>>>>>> invalid options (long) " ;;
         esac
       OPTIND=1
       shift
      ;;
    ? )  _usage "Short: >>>>>>>> invalid options (short) "  ;;
  esac
done

산출량

##################################################################
echo "----------------------------------------------------------"
echo "RESULT short-foo      : $sfoo                                    long-foo      : $lfoo"
echo "RESULT short-bar      : $sbar                                    long-bar      : $lbar"
echo "RESULT short-foobar   : $sfoobar                                 long-foobar   : $lfoobar"
echo "RESULT short-barfoo   : $sbarfoo                                 long-barfoo   : $lbarfoo"
echo "RESULT short-arguments: $sarguments  with Argument = \"$sARG\"   long-arguments: $larguments and $lARG"

위의 내용을 하나의 통합된 스크립트로 결합

#!/bin/bash
# foobar: getopts with short and long options AND arguments

function _cleanup ()
{
  unset -f _usage _cleanup ; return 0
}

## Clear out nested functions on exit
trap _cleanup INT EXIT RETURN

###### some declarations for this example ######
Options=$@
Optnum=$#
sfoo='no '
sbar='no '
sfoobar='no '
sbarfoo='no '
sarguments='no '
sARG=empty
lfoo='no '
lbar='no '
lfoobar='no '
lbarfoo='no '
larguments='no '
lARG=empty

function _usage() 
{
  ###### U S A G E : Help and ERROR ######
  cat <<EOF
   foobar $Options
   $*
          Usage: foobar <[options]>
          Options:
                  -b   --bar            Set bar to yes    ($foo)
                    -f   --foo            Set foo to yes    ($bart)
                      -h   --help           Show this message
                  -A   --arguments=...  Set arguments to yes ($arguments) AND get ARGUMENT ($ARG)
                  -B   --barfoo         Set barfoo to yes ($barfoo)
                  -F   --foobar         Set foobar to yes ($foobar)
EOF
}

[ $# = 0 ] && _usage "  >>>>>>>> no options given "

##################################################################    
#######  "getopts" with: short options  AND  long options  #######
#######            AND  short/long arguments               #######
while getopts ':bfh-A:BF' OPTION ; do
  case "$OPTION" in
    b  ) sbar=yes                       ;;
    f  ) sfoo=yes                       ;;
    h  ) _usage                         ;;   
    A  ) sarguments=yes;sARG="$OPTARG"  ;;
    B  ) sbarfoo=yes                    ;;
    F  ) sfoobar=yes                    ;;
    -  ) [ $OPTIND -ge 1 ] && optind=$(expr $OPTIND - 1 ) || optind=$OPTIND
         eval OPTION="\$$optind"
         OPTARG=$(echo $OPTION | cut -d'=' -f2)
         OPTION=$(echo $OPTION | cut -d'=' -f1)
         case $OPTION in
             --foo       ) lfoo=yes                       ;;
             --bar       ) lbar=yes                       ;;
             --foobar    ) lfoobar=yes                    ;;
             --barfoo    ) lbarfoo=yes                    ;;
             --help      ) _usage                         ;;
               --arguments ) larguments=yes;lARG="$OPTARG"  ;; 
             * )  _usage " Long: >>>>>>>> invalid options (long) " ;;
         esac
       OPTIND=1
       shift
      ;;
    ? )  _usage "Short: >>>>>>>> invalid options (short) "  ;;
  esac
done

휴대용 셸 라이브러리인 shFlags(Linux, Solaris 등에서 sh, bash, dash, ksh, zsh 등을 의미)를 살펴봅니다.

스크립트에 한 줄을 추가하는 것처럼 새 플래그를 쉽게 추가할 수 있으며 자동 생성된 사용 기능을 제공합니다.

여기 간단한 것이 있습니다.Hello, world!shFlag 사용:

#!/bin/sh

# source shflags from current directory
. ./shflags

# define a 'name' command-line string flag
DEFINE_string 'name' 'world' 'name to say hello to' 'n'

# parse the command-line
FLAGS "$@" || exit 1
eval set -- "${FLAGS_ARGV}"

# say hello
echo "Hello, ${FLAGS_name}!"

긴 옵션(예: 리눅스)을 지원하는 향상된 getopt가 있는 OS의 경우 다음을 수행할 수 있습니다.

$ ./hello_world.sh --name Kate
Hello, Kate!

나머지의 경우 다음과 같은 짧은 옵션을 사용해야 합니다.

$ ./hello_world.sh -n Kate
Hello, Kate!

새 플래그를 추가하는 것은 새 플래그를 추가하는 것만큼 간단합니다.DEFINE_ call.

다른 방법은...

# translate long options to short
for arg
do
    delim=""
    case "$arg" in
       --help) args="${args}-h ";;
       --verbose) args="${args}-v ";;
       --config) args="${args}-c ";;
       # pass through anything else
       *) [[ "${arg:0:1}" == "-" ]] || delim="\""
           args="${args}${delim}${arg}${delim} ";;
    esac
done
# reset the translated args
eval set -- $args
# now we can process with getopt
while getopts ":hvc:" opt; do
    case $opt in
        h)  usage ;;
        v)  VERBOSE=true ;;
        c)  source $OPTARG ;;
        \?) usage ;;
        :)
        echo "option -$OPTARG requires an argument"
        usage
        ;;
    esac
done

저는 이런 식으로 해결했습니다.

# A string with command options
options=$@

# An array with all the arguments
arguments=($options)

# Loop index
index=0

for argument in $options
  do
    # Incrementing index
    index=`expr $index + 1`

    # The conditions
    case $argument in
      -a) echo "key $argument value ${arguments[index]}" ;;
      -abc) echo "key $argument value ${arguments[index]}" ;;
    esac
  done

exit;

제가 바보 같은 건가요? getopt그리고.getopts너무 혼란스러워요.

만약 당신이 원하지 않는다면.getopt종속성, 다음을 수행할 수 있습니다.

while test $# -gt 0
do
  case $1 in

  # Normal option processing
    -h | --help)
      # usage and help
      ;;
    -v | --version)
      # version info
      ;;
  # ...

  # Special cases
    --)
      break
      ;;
    --*)
      # error unknown (long) option $1
      ;;
    -?)
      # error unknown (short) option $1
      ;;

  # FUN STUFF HERE:
  # Split apart combined short options
    -*)
      split=$1
      shift
      set -- $(echo "$split" | cut -c 2- | sed 's/./-& /g') "$@"
      continue
      ;;

  # Done with options
    *)
      break
      ;;
  esac

  # for testing purposes:
  echo "$1"

  shift
done

물론, 그러면 하나의 대시로 긴 스타일 옵션을 사용할 수 없습니다.단축된 버전(예: --verbose 대신 --verbos)을 추가하려면 수동으로 추가해야 합니다.

하지만 당신이 얻고자 한다면,getopts긴 옵션과 함께 기능성을 제공합니다. 이것은 간단한 방법입니다.

저는 또한 이 토막글을 요지에 넣었습니다.

제공되는 트인getopts할 수 없습니다.이를 수행할 수 있는 외부 getopt(1) 프로그램이 있지만 리눅스에서는 util-linux 패키지에서만 얻을 수 있습니다.예제 스크립트 getopt-parse.bash와 함께 제공됩니다.

셸 함수로 작성된 것도 있습니다.

#!/bin/bash
while getopts "abc:d:" flag
do
  case $flag in
    a) echo "[getopts:$OPTIND]==> -$flag";;
    b) echo "[getopts:$OPTIND]==> -$flag";;
    c) echo "[getopts:$OPTIND]==> -$flag $OPTARG";;
    d) echo "[getopts:$OPTIND]==> -$flag $OPTARG";;
  esac
done

shift $((OPTIND-1))
echo "[otheropts]==> $@"

exit

.

#!/bin/bash
until [ -z "$1" ]; do
  case $1 in
    "--dlong")
      shift
      if [ "${1:1:0}" != "-" ]
      then
        echo "==> dlong $1"
        shift
      fi;;
    *) echo "==> other $1"; shift;;
  esac
done
exit

수락된 답변은 bash 기본 제공의 모든 단점을 지적하는 매우 멋진 작업을 수행합니다.getopts답은 다음으로 끝납니다.

따라서 긴 옵션에 대한 지원 부족을 해결하기 위해 더 많은 코드를 작성할 수 있지만, 이는 훨씬 더 많은 작업이며 코드를 단순화하기 위해 getopt 파서를 사용하는 목적을 부분적으로 무시합니다.

그리고 저는 원칙적으로 그 진술에 동의하지만, 우리 모두가 다양한 스크립트에서 이 기능을 구현한 횟수가 "표준화된" 잘 테스트된 솔루션을 만드는 데 약간의 노력을 기울이는 것을 정당화한다고 생각합니다.

저는 업를했같이, 내된장이다드니 "습레그와이"에 .getopts외부 종속성 없이 순수 bash로 구현합니다.이 기능의 사용은 내장된 기능과 100% 호환됩니다.getopts.

포함으써로를 으로써.getopts_long(GitHub에서 호스팅됨) 스크립트에서 원래 질문에 대한 답변은 다음과 같이 간단하게 구현할 수 있습니다.

source "${PATH_TO}/getopts_long.bash"

while getopts_long ':c: copyfile:' OPTKEY; do
    case ${OPTKEY} in
        'c'|'copyfile')
            echo 'file supplied -- ${OPTARG}'
            ;;
        '?')
            echo "INVALID OPTION -- ${OPTARG}" >&2
            exit 1
            ;;
        ':')
            echo "MISSING ARGUMENT for option -- ${OPTARG}" >&2
            exit 1
            ;;
        *)
            echo "Misconfigured OPTSPEC or uncaught option -- ${OPTKEY}" >&2
            exit 1
            ;;
    esac
done

shift $(( OPTIND - 1 ))
[[ "${1}" == "--" ]] && shift

ksh93,getopts긴 이름을 지원합니다...

while getopts "f(file):s(server):" flag
do
    echo "$flag" $OPTIND $OPTARG
done

그래서 제가 찾은 튜토리얼은 이렇게 말했습니다.한 번 해보고 보세요.

또 다른 버전의 바퀴를 발명하는 것은...

이 함수는 GNU getopt를 위한 POSIX 호환 플레인 본 셸 대체입니다.필수/옵션/없음 인수를 허용할 수 있는 짧은/긴 옵션을 지원하며, 옵션이 지정되는 방식은 GNU getopt와 거의 동일하므로 변환이 사소한 것입니다.

물론 이것은 스크립트에 넣기에는 여전히 상당한 코드 덩어리이지만, 잘 알려진 getopt_long 셸 함수의 약 절반 줄이며, 기존 GNU getopt 사용을 대체하고 싶은 경우에 더 선호될 수 있습니다.

이것은 꽤 새로운 코드이기 때문에 YMMV (그리고 만약 이것이 실제로 POSIX와 호환되지 않는다면 확실히 알려주세요 - 휴대성은 처음부터 의도되었지만, 저는 유용한 POSIX 테스트 환경을 가지고 있지 않습니다.)

코드 및 사용 예는 다음과 같습니다.

#!/bin/sh
# posix_getopt shell function
# Author: Phil S.
# Version: 1.0
# Created: 2016-07-05
# URL: http://stackoverflow.com/a/37087374/324105

# POSIX-compatible argument quoting and parameter save/restore
# http://www.etalabs.net/sh_tricks.html
# Usage:
# parameters=$(save "$@") # save the original parameters.
# eval "set -- ${parameters}" # restore the saved parameters.
save () {
    local param
    for param; do
        printf %s\\n "$param" \
            | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/"
    done
    printf %s\\n " "
}

# Exit with status $1 after displaying error message $2.
exiterr () {
    printf %s\\n "$2" >&2
    exit $1
}

# POSIX-compatible command line option parsing.
# This function supports long options and optional arguments, and is
# a (largely-compatible) drop-in replacement for GNU getopt.
#
# Instead of:
# opts=$(getopt -o "$shortopts" -l "$longopts" -- "$@")
# eval set -- ${opts}
#
# We instead use:
# opts=$(posix_getopt "$shortopts" "$longopts" "$@")
# eval "set -- ${opts}"
posix_getopt () { # args: "$shortopts" "$longopts" "$@"
    local shortopts longopts \
          arg argtype getopt nonopt opt optchar optword suffix

    shortopts="$1"
    longopts="$2"
    shift 2

    getopt=
    nonopt=
    while [ $# -gt 0 ]; do
        opt=
        arg=
        argtype=
        case "$1" in
            # '--' means don't parse the remaining options
            ( -- ) {
                getopt="${getopt}$(save "$@")"
                shift $#
                break
            };;
            # process short option
            ( -[!-]* ) {         # -x[foo]
                suffix=${1#-?}   # foo
                opt=${1%$suffix} # -x
                optchar=${opt#-} # x
                case "${shortopts}" in
                    ( *${optchar}::* ) { # optional argument
                        argtype=optional
                        arg="${suffix}"
                        shift
                    };;
                    ( *${optchar}:* ) { # required argument
                        argtype=required
                        if [ -n "${suffix}" ]; then
                            arg="${suffix}"
                            shift
                        else
                            case "$2" in
                                ( -* ) exiterr 1 "$1 requires an argument";;
                                ( ?* ) arg="$2"; shift 2;;
                                (  * ) exiterr 1 "$1 requires an argument";;
                            esac
                        fi
                    };;
                    ( *${optchar}* ) { # no argument
                        argtype=none
                        arg=
                        shift
                        # Handle multiple no-argument parameters combined as
                        # -xyz instead of -x -y -z. If we have just shifted
                        # parameter -xyz, we now replace it with -yz (which
                        # will be processed in the next iteration).
                        if [ -n "${suffix}" ]; then
                            eval "set -- $(save "-${suffix}")$(save "$@")"
                        fi
                    };;
                    ( * ) exiterr 1 "Unknown option $1";;
                esac
            };;
            # process long option
            ( --?* ) {            # --xarg[=foo]
                suffix=${1#*=}    # foo (unless there was no =)
                if [ "${suffix}" = "$1" ]; then
                    suffix=
                fi
                opt=${1%=$suffix} # --xarg
                optword=${opt#--} # xarg
                case ",${longopts}," in
                    ( *,${optword}::,* ) { # optional argument
                        argtype=optional
                        arg="${suffix}"
                        shift
                    };;
                    ( *,${optword}:,* ) { # required argument
                        argtype=required
                        if [ -n "${suffix}" ]; then
                            arg="${suffix}"
                            shift
                        else
                            case "$2" in
                                ( -* ) exiterr 1 \
                                       "--${optword} requires an argument";;
                                ( ?* ) arg="$2"; shift 2;;
                                (  * ) exiterr 1 \
                                       "--${optword} requires an argument";;
                            esac
                        fi
                    };;
                    ( *,${optword},* ) { # no argument
                        if [ -n "${suffix}" ]; then
                            exiterr 1 "--${optword} does not take an argument"
                        fi
                        argtype=none
                        arg=
                        shift
                    };;
                    ( * ) exiterr 1 "Unknown option $1";;
                esac
            };;
            # any other parameters starting with -
            ( -* ) exiterr 1 "Unknown option $1";;
            # remember non-option parameters
            ( * ) nonopt="${nonopt}$(save "$1")"; shift;;
        esac

        if [ -n "${opt}" ]; then
            getopt="${getopt}$(save "$opt")"
            case "${argtype}" in
                ( optional|required ) {
                    getopt="${getopt}$(save "$arg")"
                };;
            esac
        fi
    done

    # Generate function output, suitable for:
    # eval "set -- $(posix_getopt ...)"
    printf %s "${getopt}"
    if [ -n "${nonopt}" ]; then
        printf %s "$(save "--")${nonopt}"
    fi
}

사용 예:

# Process command line options
shortopts="hvd:c::s::L:D"
longopts="help,version,directory:,client::,server::,load:,delete"
#opts=$(getopt -o "$shortopts" -l "$longopts" -n "$(basename $0)" -- "$@")
opts=$(posix_getopt "$shortopts" "$longopts" "$@")
if [ $? -eq 0 ]; then
    #eval set -- ${opts}
    eval "set -- ${opts}"
    while [ $# -gt 0 ]; do
        case "$1" in
            ( --                ) shift; break;;
            ( -h|--help         ) help=1; shift; break;;
            ( -v|--version      ) version_help=1; shift; break;;
            ( -d|--directory    ) dir=$2; shift 2;;
            ( -c|--client       ) useclient=1; client=$2; shift 2;;
            ( -s|--server       ) startserver=1; server_name=$2; shift 2;;
            ( -L|--load         ) load=$2; shift 2;;
            ( -D|--delete       ) delete=1; shift;;
        esac
    done
else
    shorthelp=1 # getopt returned (and reported) an error.
fi

저는 가끔 셸 스크립트만 작성하고 연습에서 빠지기 때문에 어떤 피드백도 감사합니다.

@Arvid Requate에서 제안한 전략을 사용하여 몇 가지 사용자 오류를 발견했습니다.값을 포함하는 것을 잊은 사용자는 실수로 다음 옵션의 이름을 값으로 처리하게 됩니다.

./getopts_test.sh --loglevel= --toc=TRUE

그러면 "log level" 값이 "--toc=TRUE"로 표시됩니다.이것은 피할 수 있습니다.

수동 파싱에 대한 http://mwiki.wooledge.org/BashFAQ/035 토론에서 CLI에 대한 사용자 오류 확인에 대한 몇 가지 아이디어를 수정했습니다."-"와 "--" 인수를 모두 처리하는 데 오류 검사를 삽입했습니다.

그리고 나서 저는 구문을 만지작거리기 시작했습니다. 그래서 여기에 있는 모든 오류들은 전적으로 제 잘못이지, 원작자들의 잘못이 아닙니다.

나의 접근 방식은 등호를 사용하거나 사용하지 않고 길게 입력하는 것을 선호하는 사용자를 도와줍니다.즉, "--log level 9"에 대한 응답이 "--log level=9"와 같아야 합니다.--/공간 방법에서는 사용자가 인수를 잊어버렸는지 확실히 알 수 없으므로 추측이 필요합니다.

  1. 사용자가 long/sign 형식(--opt=)을 가지고 있으면 = 뒤에 공백이 있으면 인수가 제공되지 않았기 때문에 오류가 발생합니다.
  2. 사용자에게 긴/공백 인수(--opt)가 있는 경우 이 스크립트는 인수가 뒤따르지 않거나(명령 끝) 인수가 대시로 시작되는 경우 실패를 발생시킵니다.

이 작업을 시작하는 경우 "--opt=value" 형식과 "--opt value" 형식 사이에는 흥미로운 차이가 있습니다.등호를 사용하면 명령줄 인수가 "opt=value"로 표시되고 문자열 구문 분석을 처리하는 작업이 "="로 구분됩니다.반대로 "--opt value"의 경우 인수 이름이 "opt"이며 명령줄에서 다음 값을 제공해야 하는 문제가 있습니다.여기서 @Arvid Requate는 간접 참조인 ${!OPTIND를 사용했습니다.저는 여전히 그것을 전혀 이해하지 못하고, BashFAQ의 댓글은 그러한 스타일에 대해 경고하는 것 같습니다(http://mywiki.wooledge.org/BashFAQ/006) .참고로 OPTIND=$(($OPTIND + 1))의 중요성에 대한 이전 포스터의 언급이 정확하지 않은 것 같습니다.제 말은, 저는 그것을 생략해도 나쁠 것이 없다고 생각합니다.

이 스크립트의 최신 버전에서 flag -v는 VERBOSE 인쇄물을 의미합니다.

"cli-5.sh "이라는 파일에 저장하거나, 실행 파일로 만들면 이러한 파일 중 하나가 작동하거나, 원하는 방식으로 실패합니다.

./cli-5.sh  -v --loglevel=44 --toc  TRUE
./cli-5.sh  -v --loglevel=44 --toc=TRUE
./cli-5.sh --loglevel 7
./cli-5.sh --loglevel=8
./cli-5.sh -l9

./cli-5.sh  --toc FALSE --loglevel=77
./cli-5.sh  --toc=FALSE --loglevel=77

./cli-5.sh   -l99 -t yyy
./cli-5.sh   -l 99 -t yyy

다음은 사용자 intpu에 대한 오류 검사의 출력 예입니다.

$ ./cli-5.sh  --toc --loglevel=77
ERROR: toc value must not have dash at beginning
$ ./cli-5.sh  --toc= --loglevel=77
ERROR: value for toc undefined

-v는 OPTIND와 OPTARG의 내부를 출력하기 때문에 켜는 것을 고려해야 합니다.

#/usr/bin/env bash

## Paul Johnson
## 20171016
##

## Combines ideas from
## https://stackoverflow.com/questions/402377/using-getopts-in-bash-shell-script-to-get-long-and-short-command-line-options
## by @Arvid Requate, and http://mwiki.wooledge.org/BashFAQ/035

# What I don't understand yet: 
# In @Arvid REquate's answer, we have 
# val="${!OPTIND}"; OPTIND=$(( $OPTIND + 1 ))
# this works, but I don't understand it!


die() {
    printf '%s\n' "$1" >&2
    exit 1
}

printparse(){
    if [ ${VERBOSE} -gt 0 ]; then
        printf 'Parse: %s%s%s\n' "$1" "$2" "$3" >&2;
    fi
}

showme(){
    if [ ${VERBOSE} -gt 0 ]; then
        printf 'VERBOSE: %s\n' "$1" >&2;
    fi
}


VERBOSE=0
loglevel=0
toc="TRUE"

optspec=":vhl:t:-:"
while getopts "$optspec" OPTCHAR; do

    showme "OPTARG:  ${OPTARG[*]}"
    showme "OPTIND:  ${OPTIND[*]}"
    case "${OPTCHAR}" in
        -)
            case "${OPTARG}" in
                loglevel) #argument has no equal sign
                    opt=${OPTARG}
                    val="${!OPTIND}"
                    ## check value. If negative, assume user forgot value
                    showme "OPTIND is {$OPTIND} {!OPTIND} has value \"${!OPTIND}\""
                    if [[ "$val" == -* ]]; then
                        die "ERROR: $opt value must not have dash at beginning"
                    fi
                    ## OPTIND=$(( $OPTIND + 1 )) # CAUTION! no effect?
                    printparse "--${OPTARG}" "  " "${val}"
                    loglevel="${val}"
                    shift
                    ;;
                loglevel=*) #argument has equal sign
                    opt=${OPTARG%=*}
                    val=${OPTARG#*=}
                    if [ "${OPTARG#*=}" ]; then
                        printparse "--${opt}" "=" "${val}"
                        loglevel="${val}"
                        ## shift CAUTION don't shift this, fails othewise
                    else
                        die "ERROR: $opt value must be supplied"
                    fi
                    ;;
                toc) #argument has no equal sign
                    opt=${OPTARG}
                    val="${!OPTIND}"
                    ## check value. If negative, assume user forgot value
                    showme "OPTIND is {$OPTIND} {!OPTIND} has value \"${!OPTIND}\""
                    if [[ "$val" == -* ]]; then
                        die "ERROR: $opt value must not have dash at beginning"
                    fi
                    ## OPTIND=$(( $OPTIND + 1 )) #??
                    printparse "--${opt}" " " "${val}"
                    toc="${val}"
                    shift
                    ;;
                toc=*) #argument has equal sign
                    opt=${OPTARG%=*}
                    val=${OPTARG#*=}
                    if [ "${OPTARG#*=}" ]; then
                        toc=${val}
                        printparse "--$opt" " -> " "$toc"
                        ##shift ## NO! dont shift this
                    else
                        die "ERROR: value for $opt undefined"
                    fi
                    ;;

                help)
                    echo "usage: $0 [-v] [--loglevel[=]<value>] [--toc[=]<TRUE,FALSE>]" >&2
                    exit 2
                    ;;
                *)
                    if [ "$OPTERR" = 1 ] && [ "${optspec:0:1}" != ":" ]; then
                        echo "Unknown option --${OPTARG}" >&2
                    fi
                    ;;
            esac;;
        h|-\?|--help)
            ## must rewrite this for all of the arguments
            echo "usage: $0 [-v] [--loglevel[=]<value>] [--toc[=]<TRUE,FALSE>]" >&2
            exit 2
            ;;
        l)
            loglevel=${OPTARG}
            printparse "-l" " "  "${loglevel}"
            ;;
        t)
            toc=${OPTARG}
            ;;
        v)
            VERBOSE=1
            ;;

        *)
            if [ "$OPTERR" != 1 ] || [ "${optspec:0:1}" = ":" ]; then
                echo "Non-option argument: '-${OPTARG}'" >&2
            fi
            ;;
    esac
done



echo "
After Parsing values
"
echo "loglevel  $loglevel" 
echo "toc  $toc"

저는 아직 그의 해결책에 대해 의견을 제시하거나 투표할 충분한 담당자가 없지만, 저에게는 sme의 답변이 매우 잘 작동했습니다.제가 마주친 유일한 문제는 논쟁이 결국 단일 인용문으로 포장된다는 것입니다(그래서 저는 그것들을 제거했습니다).

몇 가지 예제 사용법과 도움말 텍스트도 추가했습니다.여기에 약간 확장된 버전을 포함하겠습니다.

#!/bin/bash

# getopt example
# from: https://stackoverflow.com/questions/402377/using-getopts-in-bash-shell-script-to-get-long-and-short-command-line-options
HELP_TEXT=\
"   USAGE:\n
    Accepts - and -- flags, can specify options that require a value, and can be in any order. A double-hyphen (--) will stop processing options.\n\n

    Accepts the following forms:\n\n

    getopt-example.sh -a -b -c value-for-c some-arg\n
    getopt-example.sh -c value-for-c -a -b some-arg\n
    getopt-example.sh -abc some-arg\n
    getopt-example.sh --along --blong --clong value-for-c -a -b -c some-arg\n
    getopt-example.sh some-arg --clong value-for-c\n
    getopt-example.sh
"

aflag=false
bflag=false
cargument=""

# options may be followed by one colon to indicate they have a required argument
if ! options=$(getopt -o abc:h\? -l along,blong,help,clong: -- "$@")
then
    # something went wrong, getopt will put out an error message for us
    exit 1
fi

set -- $options

while [ $# -gt 0 ]
do
    case $1 in
    -a|--along) aflag=true ;;
    -b|--blong) bflag=true ;;
    # for options with required arguments, an additional shift is required
    -c|--clong) cargument="$2" ; shift;;
    -h|--help|-\?) echo -e $HELP_TEXT; exit;;
    (--) shift; break;;
    (-*) echo "$0: error - unrecognized option $1" 1>&2; exit 1;;
    (*) break;;
    esac
    shift
done

# to remove the single quotes around arguments, pipe the output into:
# | sed -e "s/^'\\|'$//g"  (just leading/trailing) or | sed -e "s/'//g"  (all)

echo aflag=${aflag}
echo bflag=${bflag}
echo cargument=${cargument}

while [ $# -gt 0 ]
do
    echo arg=$1
    shift

    if [[ $aflag == true ]]; then
        echo a is true
    fi

done

여기에서 bash의 복잡한 옵션 구문 분석을 위한 몇 가지 다른 접근법을 찾을 수 있습니다. http://mywiki.wooledge.org/ComplexOptionParsing

저는 다음과 같은 것을 만들었습니다. 저는 그것이 좋은 것이라고 생각합니다. 왜냐하면 그것은 최소한의 코드이고 긴 옵션과 짧은 옵션이 모두 작동하기 때문입니다.긴 옵션은 이 접근 방식을 사용하여 여러 개의 인수를 가질 수도 있습니다.

#!/bin/bash
# Uses bash extensions.  Not portable as written.

declare -A longoptspec
longoptspec=( [loglevel]=1 ) #use associative array to declare how many arguments a long option expects, in this case we declare that loglevel expects/has one argument, long options that aren't listed i n this way will have zero arguments by default
optspec=":h-:"
while getopts "$optspec" opt; do
while true; do
    case "${opt}" in
        -) #OPTARG is name-of-long-option or name-of-long-option=value
            if [[ "${OPTARG}" =~ .*=.* ]] #with this --key=value format only one argument is possible
            then
                opt=${OPTARG/=*/}
                OPTARG=${OPTARG#*=}
                ((OPTIND--))    
            else #with this --key value1 value2 format multiple arguments are possible
                opt="$OPTARG"
                OPTARG=(${@:OPTIND:$((longoptspec[$opt]))})
            fi
            ((OPTIND+=longoptspec[$opt]))
            continue #now that opt/OPTARG are set we can process them as if getopts would've given us long options
            ;;
        loglevel)
          loglevel=$OPTARG
            ;;
        h|help)
            echo "usage: $0 [--loglevel[=]<value>]" >&2
            exit 2
            ;;
    esac
break; done
done

# End of file

단순히 이것이 당신이 스크립트를 부르고 싶은 방식이라면.

myscript.sh --input1 "ABC" --input2 "PQR" --input2 "XYZ"

그런 다음 getopt와 --longoptions의 도움을 받아 이 간단한 방법으로 달성할 수 있습니다.

이것을 사용해 보세요, 이것이 유용하기를 바랍니다.

# Read command line options
ARGUMENT_LIST=(
    "input1"
    "input2"
    "input3"
)



# read arguments
opts=$(getopt \
    --longoptions "$(printf "%s:," "${ARGUMENT_LIST[@]}")" \
    --name "$(basename "$0")" \
    --options "" \
    -- "$@"
)


echo $opts

eval set --$opts

while true; do
    case "$1" in
    --input1)  
        shift
        empId=$1
        ;;
    --input2)  
        shift
        fromDate=$1
        ;;
    --input3)  
        shift
        toDate=$1
        ;;
      --)
        shift
        break
        ;;
    esac
    shift
done

저는 꽤 오랫동안 그 주제에 대해 연구해 왔습니다.그리고 당신이 메인 스크립트에서 소스해야 할 나만의 라이브러리를 만들었습니다.예는 libpt4shellcd2mpc를 참조하십시오.도움이 되길 바랍니다!

개선된 솔루션:

# translate long options to short
# Note: This enable long options but disable "--?*" in $OPTARG, or disable long options after  "--" in option fields.
for ((i=1;$#;i++)) ; do
    case "$1" in
        --)
            # [ ${args[$((i-1))]} == ... ] || EndOpt=1 ;;& # DIRTY: we still can handle some execptions...
            EndOpt=1 ;;&
        --version) ((EndOpt)) && args[$i]="$1"  || args[$i]="-V";;
        # default case : short option use the first char of the long option:
        --?*) ((EndOpt)) && args[$i]="$1"  || args[$i]="-${1:2:1}";;
        # pass through anything else:
        *) args[$i]="$1" ;;
    esac
    shift
done
# reset the translated args
set -- "${args[@]}"

function usage {
echo "Usage: $0 [options] files" >&2
    exit $1
}

# now we can process with getopt
while getopts ":hvVc:" opt; do
    case $opt in
        h)  usage ;;
        v)  VERBOSE=true ;;
        V)  echo $Version ; exit ;;
        c)  source $OPTARG ;;
        \?) echo "unrecognized option: -$opt" ; usage -1 ;;
        :)
        echo "option -$OPTARG requires an argument"
        usage -1
        ;;
    esac
done

shift $((OPTIND-1))
[[ "$1" == "--" ]] && shift

긴 명령줄 옵션이 필요한 경우 getopts 부분에만 ksh를 사용하는 것이 더 쉬울 수 있습니다.

# Working Getopts Long => KSH

#! /bin/ksh
# Getopts Long
USAGE="s(showconfig)"
USAGE+="c:(createdb)"
USAGE+="l:(createlistener)"
USAGE+="g:(generatescripts)"
USAGE+="r:(removedb)"
USAGE+="x:(removelistener)"
USAGE+="t:(createtemplate)"
USAGE+="h(help)"

while getopts "$USAGE" optchar ; do
    case $optchar in
    s)  echo "Displaying Configuration" ;;
        c)  echo "Creating Database $OPTARG" ;;
    l)  echo "Creating Listener LISTENER_$OPTARG" ;;
    g)  echo "Generating Scripts for Database $OPTARG" ;;
    r)  echo "Removing Database $OPTARG" ;;
    x)  echo "Removing Listener LISTENER_$OPTARG" ;;
    t)  echo "Creating Database Template" ;;
    h)  echo "Help" ;;
    esac
done

저는 외부 종속성이 없고 엄격한 bash 지원(-u)이 있는 것을 원했고, 이전 bash 버전에서도 작동하기 위해 필요했습니다.이는 다양한 유형의 매개 변수를 처리합니다.

  • 짧은 수영복(-h)
  • 짧은 옵션(-i "image.jpg")
  • 긴 풍선(--도움말)
  • equals 옵션(--file="interval.ext")
  • 공간 옵션(--파일 "vmdk.ext")
  • 포화조류(-hvm)

스크립트의 맨 위에 다음을 삽입하기만 하면 됩니다.

# Check if a list of params contains a specific param
# usage: if _param_variant "h|?|help p|path f|file long-thing t|test-thing" "file" ; then ...
# the global variable $key is updated to the long notation (last entry in the pipe delineated list, if applicable)
_param_variant() {
  for param in $1 ; do
    local variants=${param//\|/ }
    for variant in $variants ; do
      if [[ "$variant" = "$2" ]] ; then
        # Update the key to match the long version
        local arr=(${param//\|/ })
        let last=${#arr[@]}-1
        key="${arr[$last]}"
        return 0
      fi
    done
  done
  return 1
}

# Get input parameters in short or long notation, with no dependencies beyond bash
# usage:
#     # First, set your defaults
#     param_help=false
#     param_path="."
#     param_file=false
#     param_image=false
#     param_image_lossy=true
#     # Define allowed parameters
#     allowed_params="h|?|help p|path f|file i|image image-lossy"
#     # Get parameters from the arguments provided
#     _get_params $*
#
# Parameters will be converted into safe variable names like:
#     param_help,
#     param_path,
#     param_file,
#     param_image,
#     param_image_lossy
#
# Parameters without a value like "-h" or "--help" will be treated as
# boolean, and will be set as param_help=true
#
# Parameters can accept values in the various typical ways:
#     -i "path/goes/here"
#     --image "path/goes/here"
#     --image="path/goes/here"
#     --image=path/goes/here
# These would all result in effectively the same thing:
#     param_image="path/goes/here"
#
# Concatinated short parameters (boolean) are also supported
#     -vhm is the same as -v -h -m
_get_params(){

  local param_pair
  local key
  local value
  local shift_count

  while : ; do
    # Ensure we have a valid param. Allows this to work even in -u mode.
    if [[ $# == 0 || -z $1 ]] ; then
      break
    fi

    # Split the argument if it contains "="
    param_pair=(${1//=/ })
    # Remove preceeding dashes
    key="${param_pair[0]#--}"

    # Check for concatinated boolean short parameters.
    local nodash="${key#-}"
    local breakout=false
    if [[ "$nodash" != "$key" && ${#nodash} -gt 1 ]]; then
      # Extrapolate multiple boolean keys in single dash notation. ie. "-vmh" should translate to: "-v -m -h"
      local short_param_count=${#nodash}
      let new_arg_count=$#+$short_param_count-1
      local new_args=""
      # $str_pos is the current position in the short param string $nodash
      for (( str_pos=0; str_pos<new_arg_count; str_pos++ )); do
        # The first character becomes the current key
        if [ $str_pos -eq 0 ] ; then
          key="${nodash:$str_pos:1}"
          breakout=true
        fi
        # $arg_pos is the current position in the constructed arguments list
        let arg_pos=$str_pos+1
        if [ $arg_pos -gt $short_param_count ] ; then
          # handle other arguments
          let orignal_arg_number=$arg_pos-$short_param_count+1
          local new_arg="${!orignal_arg_number}"
        else
          # break out our one argument into new ones
          local new_arg="-${nodash:$str_pos:1}"
        fi
        new_args="$new_args \"$new_arg\""
      done
      # remove the preceding space and set the new arguments
      eval set -- "${new_args# }"
    fi
    if ! $breakout ; then
      key="$nodash"
    fi

    # By default we expect to shift one argument at a time
    shift_count=1
    if [ "${#param_pair[@]}" -gt "1" ] ; then
      # This is a param with equals notation
      value="${param_pair[1]}"
    else
      # This is either a boolean param and there is no value,
      # or the value is the next command line argument
      # Assume the value is a boolean true, unless the next argument is found to be a value.
      value=true
      if [[ $# -gt 1 && -n "$2" ]]; then
        local nodash="${2#-}"
        if [ "$nodash" = "$2" ]; then
          # The next argument has NO preceding dash so it is a value
          value="$2"
          shift_count=2
        fi
      fi
    fi

    # Check that the param being passed is one of the allowed params
    if _param_variant "$allowed_params" "$key" ; then
      # --key-name will now become param_key_name
      eval param_${key//-/_}="$value"
    else
      printf 'WARNING: Unknown option (ignored): %s\n' "$1" >&2
    fi
    shift $shift_count
  done
}

다음과 같이 사용합니다.

# Assign defaults for parameters
param_help=false
param_path=$(pwd)
param_file=false
param_image=true
param_image_lossy=true
param_image_lossy_quality=85

# Define the params we will allow
allowed_params="h|?|help p|path f|file i|image image-lossy image-lossy-quality"

# Get the params from arguments provided
_get_params $*

시간이 좀 걸렸지만, 저는 모든 것을 원했습니다.

  • 단기 선택권
  • 장기 선택권
  • 논쟁이 있든 없든
  • 비옵션 인수("-" 또는 "-"가 없는 인수)
  • 하지 않습니다.script.sh /file -V또는script.sh -V /file)
  • 잘못된 용법을 알아채다.
  • 여러 줄의 코드를 변경할 필요 없이 다른 스크립트에서 모듈로 사용

마침내 저는 다음과 같은 해결책을 생각해냈습니다.getopt에 오류가 없는 상태로 합니다.getopts짧은 옵션과 긴 옵션을 구문 분석합니다.

모든 옵션은 긴 옵션 이름을 변수 이름으로 사용하여 자동으로 구문 분석됩니다(예 참조).

# create string of short options
opt_short=$(printf "%s" "${!options[@]}")

# create string of long options
opt_long="$(printf ",%s" "${options[@]}")"

# catch wrong options and move non-options to the end of the string
args=$(getopt -l "$opt_long" "$opt_short" "$@" 2> >(sed -e 's/^/stderr/g')) || echo -n "Error: " && echo "$args" | grep -oP "(?<=^stderr).*" && exit 1

# create new array of options
mapfile -t args < <(xargs -n1 <<< "$(echo "$args" | sed -E "s/(--[^ ]+) '/\1='/g")" )

# overwrite $@ (options)
set -- "${args[@]}"

# parse options ([h]=help sets the variable "$opt_help" and [V]="" sets the variable "$opt_V")
while getopts "$opt_short-:" opt; do

  echo "$opt:$OPTARG"

  # long option
  if [[ "$opt" == "-" ]]; then

    # extract long option name
    opt="${OPTARG%%=*}"

    # extract long option argument (may be empty)
    OPTARG="${OPTARG#"$opt"}"

    # remove "=" from long option argument
    OPTARG="${OPTARG#=}"

    # set variable name
    opt=opt_$opt

  # short option without argument uses long option name as variable name
  elif [[ "${options[$opt]+x}" ]] && [[ "${options[$opt]}" ]]; then
    opt=opt_${options[$opt]} 

  # short option with argument uses long option name as variable name
  elif [[ "${options[$opt:]+x}" ]] && [[ "${options[$opt:]}" ]]; then
    opt=opt_${options[$opt:]} 

  # short option without long option name uses short option name as variable name
  else
    opt=opt_$opt
  fi

  # remove double colon
  opt="${opt%:}" 

  # options without arguments are set to 1
  [[ ! $OPTARG ]] && OPTARG=1 

  # replace hyphen against underscore
  opt="${opt//-/_}"

  # set variable variables (replaces hyphen against underscore)
  printf -v "$opt" '%s' "$OPTARG" 

done

과 이제필옵이정의됩면니다하만름션요한▁▁need▁names됩만 정의하면 .source대본:

# import options module
declare -A options=( [h]=help [f:]=file: [V]=verbose [0]=long_only: [s]="" )
source "/usr/local/bin/inc/options.sh";

# display help text
if [[ $opt_help ]]; then
  echo "help text"
  exit
fi

# output
echo "opt_help:$opt_help"
echo "opt_file:$opt_file"
echo "opt_verbose:$opt_verbose"
echo "opt_long_only:$opt_long_only"
echo "opt_short_only:$opt_s"
echo "opt_path:$1"
echo "opt_mail:$2"

스크립트를 호출하는 동안 모든 옵션과 비옵션을 완전한 무작위 순서로 전달할 수 있습니다.

#             $opt_file     $1        $2       $opt_V  $opt_long_only $opt_s
# /demo.sh --file=file.txt /dir info@example.com -V --long_only=yes -s
opt_help:1
opt_file:file.txt
opt_verbose:1
opt_long_only:yes
opt_short_only:1
opt_path=/dir
opt_mail:info@example.com

메모들

  • options add 열가추:인수를 사용하도록 설정하는 옵션 이름 뒤에 입력합니다.
  • 하지 않은 은 " " " " " 입니다.$opt_X서, 디에어X는 짧은 옵션 이름입니다.
  • 이름을 긴 합니다.[0]=long_only물론 모든 배열 인덱스는 고유해야 합니다.

사용된 기법

getopts는 긴 옵션을 구문 분석하는 데 "사용할 수 있습니다". 인수가 없을 것으로 예상되는 한...

다음은 방법입니다.

$ cat > longopt
while getopts 'e:-:' OPT; do
  case $OPT in
    e) echo echo: $OPTARG;;
    -) #long option
       case $OPTARG in
         long-option) echo long option;;
         *) echo long option: $OPTARG;;
       esac;;
  esac
done

$ bash longopt -e asd --long-option --long1 --long2 -e test
echo: asd
long option
long option: long1
long option: long2
echo: test

긴 옵션에 대한 매개 변수를 가져오는 데 OPTIND를 사용하려고 하면 getopts는 이 매개 변수를 첫 번째 옵션 없음 위치 매개 변수로 처리하고 다른 매개 변수 구문 분석을 중지합니다.이러한 경우 간단한 사례 설명을 사용하여 수동으로 처리하는 것이 좋습니다.

이것은 "항상" 작동합니다.

$ cat >longopt2
while (($#)); do
    OPT=$1
    shift
    case $OPT in
        --*) case ${OPT:2} in
            long1) echo long1 option;;
            complex) echo comples with argument $1; shift;;
        esac;;

        -*) case ${OPT:1} in
            a) echo short option a;;
            b) echo short option b with parameter $1; shift;;
        esac;;
    esac
done


$ bash longopt2 --complex abc -a --long -b test
comples with argument abc
short option a
short option b with parameter test

비록 getopts만큼 유연하지는 않지만, 대부분의 오류 검사 코드를 케이스 인스턴스 내에서 직접 수행해야 합니다.

하지만 그것은 선택사항입니다.

크로스 플랫폼 호환성을 유지하고 외부 실행 파일에 의존하지 않기 위해 다른 언어에서 코드를 가져왔습니다.

사용하기가 매우 쉽다는 것을 알게 되었습니다. 다음은 예입니다.

ArgParser::addArg "[h]elp"    false    "This list"
ArgParser::addArg "[q]uiet"   false    "Supress output"
ArgParser::addArg "[s]leep"   1        "Seconds to sleep"
ArgParser::addArg "v"         1        "Verbose mode"

ArgParser::parse "$@"

ArgParser::isset help && ArgParser::showArgs

ArgParser::isset "quiet" \
   && echo "Quiet!" \
   || echo "Noisy!"

local __sleep
ArgParser::tryAndGetArg sleep into __sleep \
   && echo "Sleep for $__sleep seconds" \
   || echo "No value passed for sleep"

# This way is often more convienient, but is a little slower
echo "Sleep set to: $( ArgParser::getArg sleep )"

필요한 BASH는 가능한 것보다 조금 더 길지만 BASH 4의 연관 어레이에 의존하지 않기를 원했습니다.http://nt4.com/bash/argparser.inc.sh 에서 직접 다운로드할 수도 있습니다.

#!/usr/bin/env bash

# Updates to this script may be found at
# http://nt4.com/bash/argparser.inc.sh

# Example of runtime usage:
# mnc.sh --nc -q Caprica.S0*mkv *.avi *.mp3 --more-options here --host centos8.host.com

# Example of use in script (see bottom)
# Just include this file in yours, or use
# source argparser.inc.sh

unset EXPLODED
declare -a EXPLODED
function explode 
{
    local c=$# 
    (( c < 2 )) && 
    {
        echo function "$0" is missing parameters 
        return 1
    }

    local delimiter="$1"
    local string="$2"
    local limit=${3-99}

    local tmp_delim=$'\x07'
    local delin=${string//$delimiter/$tmp_delim}
    local oldifs="$IFS"

    IFS="$tmp_delim"
    EXPLODED=($delin)
    IFS="$oldifs"
}


# See: http://fvue.nl/wiki/Bash:_Passing_variables_by_reference
# Usage: local "$1" && upvar $1 "value(s)"
upvar() {
    if unset -v "$1"; then           # Unset & validate varname
        if (( $# == 2 )); then
            eval $1=\"\$2\"          # Return single value
        else
            eval $1=\(\"\${@:2}\"\)  # Return array
        fi
    fi
}

function decho
{
    :
}

function ArgParser::check
{
    __args=${#__argparser__arglist[@]}
    for (( i=0; i<__args; i++ ))
    do
        matched=0
        explode "|" "${__argparser__arglist[$i]}"
        if [ "${#1}" -eq 1 ]
        then
            if [ "${1}" == "${EXPLODED[0]}" ]
            then
                decho "Matched $1 with ${EXPLODED[0]}"
                matched=1

                break
            fi
        else
            if [ "${1}" == "${EXPLODED[1]}" ]
            then
                decho "Matched $1 with ${EXPLODED[1]}"
                matched=1

                break
            fi
        fi
    done
    (( matched == 0 )) && return 2
    # decho "Key $key has default argument of ${EXPLODED[3]}"
    if [ "${EXPLODED[3]}" == "false" ]
    then
        return 0
    else
        return 1
    fi
}

function ArgParser::set
{
    key=$3
    value="${1:-true}"
    declare -g __argpassed__$key="$value"
}

function ArgParser::parse
{

    unset __argparser__argv
    __argparser__argv=()
    # echo parsing: "$@"

    while [ -n "$1" ]
    do
        # echo "Processing $1"
        if [ "${1:0:2}" == '--' ]
        then
            key=${1:2}
            value=$2
        elif [ "${1:0:1}" == '-' ]
        then
            key=${1:1}               # Strip off leading -
            value=$2
        else
            decho "Not argument or option: '$1'" >& 2
            __argparser__argv+=( "$1" )
            shift
            continue
        fi
        # parameter=${tmp%%=*}     # Extract name.
        # value=${tmp##*=}         # Extract value.
        decho "Key: '$key', value: '$value'"
        # eval $parameter=$value
        ArgParser::check $key
        el=$?
        # echo "Check returned $el for $key"
        [ $el -eq  2 ] && decho "No match for option '$1'" >&2 # && __argparser__argv+=( "$1" )
        [ $el -eq  0 ] && decho "Matched option '${EXPLODED[2]}' with no arguments"        >&2 && ArgParser::set true "${EXPLODED[@]}"
        [ $el -eq  1 ] && decho "Matched option '${EXPLODED[2]}' with an argument of '$2'"   >&2 && ArgParser::set "$2" "${EXPLODED[@]}" && shift
        shift
    done
}

function ArgParser::isset
{
    declare -p "__argpassed__$1" > /dev/null 2>&1 && return 0
    return 1
}

function ArgParser::getArg
{
    # This one would be a bit silly, since we can only return non-integer arguments ineffeciently
    varname="__argpassed__$1"
    echo "${!varname}"
}

##
# usage: tryAndGetArg <argname> into <varname>
# returns: 0 on success, 1 on failure
function ArgParser::tryAndGetArg
{
    local __varname="__argpassed__$1"
    local __value="${!__varname}"
    test -z "$__value" && return 1
    local "$3" && upvar $3 "$__value"
    return 0
}

function ArgParser::__construct
{
    unset __argparser__arglist
    # declare -a __argparser__arglist
}

##
# @brief add command line argument
# @param 1 short and/or long, eg: [s]hort
# @param 2 default value
# @param 3 description
##
function ArgParser::addArg
{
    # check for short arg within long arg
    if [[ "$1" =~ \[(.)\] ]]
    then
        short=${BASH_REMATCH[1]}
        long=${1/\[$short\]/$short}
    else
        long=$1
    fi
    if [ "${#long}" -eq 1 ]
    then
        short=$long
        long=''
    fi
    decho short: "$short"
    decho long: "$long"
    __argparser__arglist+=("$short|$long|$1|$2|$3")
}

## 
# @brief show available command line arguments
##
function ArgParser::showArgs
{
    # declare -p | grep argparser
    printf "Usage: %s [OPTION...]\n\n" "$( basename "${BASH_SOURCE[0]}" )"
    printf "Defaults for the options are specified in brackets.\n\n";

    __args=${#__argparser__arglist[@]}
    for (( i=0; i<__args; i++ ))
    do
        local shortname=
        local fullname=
        local default=
        local description=
        local comma=

        explode "|" "${__argparser__arglist[$i]}"

        shortname="${EXPLODED[0]:+-${EXPLODED[0]}}" # String Substitution Guide: 
        fullname="${EXPLODED[1]:+--${EXPLODED[1]}}" # http://tldp.org/LDP/abs/html/parameter-substitution.html
        test -n "$shortname" \
            && test -n "$fullname" \
            && comma=","

        default="${EXPLODED[3]}"
        case $default in
            false )
                default=
                ;;
            "" )
                default=
                ;;
            * )
                default="[$default]"
        esac

        description="${EXPLODED[4]}"

        printf "  %2s%1s %-19s %s %s\n" "$shortname" "$comma" "$fullname" "$description" "$default"
    done
}

function ArgParser::test
{
    # Arguments with a default of 'false' do not take paramaters (note: default
    # values are not applied in this release)

    ArgParser::addArg "[h]elp"      false       "This list"
    ArgParser::addArg "[q]uiet" false       "Supress output"
    ArgParser::addArg "[s]leep" 1           "Seconds to sleep"
    ArgParser::addArg "v"           1           "Verbose mode"

    ArgParser::parse "$@"

    ArgParser::isset help && ArgParser::showArgs

    ArgParser::isset "quiet" \
        && echo "Quiet!" \
        || echo "Noisy!"

    local __sleep
    ArgParser::tryAndGetArg sleep into __sleep \
        && echo "Sleep for $__sleep seconds" \
        || echo "No value passed for sleep"

    # This way is often more convienient, but is a little slower
    echo "Sleep set to: $( ArgParser::getArg sleep )"

    echo "Remaining command line: ${__argparser__argv[@]}"

}

if [ "$( basename "$0" )" == "argparser.inc.sh" ]
then
    ArgParser::test "$@"
fi

모든 긴 옵션에 고유한 문자와 일치하는 문자가 짧은 옵션으로 있는 경우, 예를 들어

./slamm --chaos 23 --plenty test -quiet

와 같습니까?

./slamm -c 23 -p test -q

getopts가 $args를 다시 쓰기 전에 를 사용할 수 있습니다.

# change long options to short options

for arg; do 
    [[ "${arg:0:1}" == "-" ]] && delim="" || delim="\""
    if [ "${arg:0:2}" == "--" ]; 
       then args="${args} -${arg:2:1}" 
       else args="${args} ${delim}${arg}${delim}"
    fi
done

# reset the incoming args
eval set -- $args

# proceed as usual
while getopts ":b:la:h" OPTION; do
    .....

영감을 주셔서 mtveee에게 감사합니다.

ksh93을 제외하고 짧은 옵션만 구문 분석할 수 있지만 getopts가 긴 옵션을 처리하도록 몇 줄의 스크립트를 추가할 수 있습니다.

다음은 http://www.uxora.com/unix/shell-script/22-handle-long-options-with-getopts 에서 찾을 수 있는 코드의 일부입니다.

  #== set short options ==#
SCRIPT_OPTS=':fbF:B:-:h'
  #== set long options associated with short one ==#
typeset -A ARRAY_OPTS
ARRAY_OPTS=(
    [foo]=f
    [bar]=b
    [foobar]=F
    [barfoo]=B
    [help]=h
    [man]=h
)

  #== parse options ==#
while getopts ${SCRIPT_OPTS} OPTION ; do
    #== translate long options to short ==#
    if [[ "x$OPTION" == "x-" ]]; then
        LONG_OPTION=$OPTARG
        LONG_OPTARG=$(echo $LONG_OPTION | grep "=" | cut -d'=' -f2)
        LONG_OPTIND=-1
        [[ "x$LONG_OPTARG" = "x" ]] && LONG_OPTIND=$OPTIND || LONG_OPTION=$(echo $OPTARG | cut -d'=' -f1)
        [[ $LONG_OPTIND -ne -1 ]] && eval LONG_OPTARG="\$$LONG_OPTIND"
        OPTION=${ARRAY_OPTS[$LONG_OPTION]}
        [[ "x$OPTION" = "x" ]] &&  OPTION="?" OPTARG="-$LONG_OPTION"

        if [[ $( echo "${SCRIPT_OPTS}" | grep -c "${OPTION}:" ) -eq 1 ]]; then
            if [[ "x${LONG_OPTARG}" = "x" ]] || [[ "${LONG_OPTARG}" = -* ]]; then 
                OPTION=":" OPTARG="-$LONG_OPTION"
            else
                OPTARG="$LONG_OPTARG";
                if [[ $LONG_OPTIND -ne -1 ]]; then
                    [[ $OPTIND -le $Optnum ]] && OPTIND=$(( $OPTIND+1 ))
                    shift $OPTIND
                    OPTIND=1
                fi
            fi
        fi
    fi

    #== options follow by another option instead of argument ==#
    if [[ "x${OPTION}" != "x:" ]] && [[ "x${OPTION}" != "x?" ]] && [[ "${OPTARG}" = -* ]]; then 
        OPTARG="$OPTION" OPTION=":"
    fi

    #== manage options ==#
    case "$OPTION" in
        f  ) foo=1 bar=0                    ;;
        b  ) foo=0 bar=1                    ;;
        B  ) barfoo=${OPTARG}               ;;
        F  ) foobar=1 && foobar_name=${OPTARG} ;;
        h ) usagefull && exit 0 ;;
        : ) echo "${SCRIPT_NAME}: -$OPTARG: option requires an argument" >&2 && usage >&2 && exit 99 ;;
        ? ) echo "${SCRIPT_NAME}: -$OPTARG: unknown option" >&2 && usage >&2 && exit 99 ;;
    esac
done
shift $((${OPTIND} - 1))

다음은 테스트입니다.

# Short options test
$ ./foobar_any_getopts.sh -bF "Hello world" -B 6 file1 file2
foo=0 bar=1
barfoo=6
foobar=1 foobar_name=Hello world
files=file1 file2

# Long and short options test
$ ./foobar_any_getopts.sh --bar -F Hello --barfoo 6 file1 file2
foo=0 bar=1
barfoo=6
foobar=1 foobar_name=Hello
files=file1 file2

그렇지 않으면 최근의 콘 셸 ksh93에서getopts긴 옵션을 자연스럽게 구문 분석할 수 있으며 맨 페이지도 마찬가지로 표시할 수 있습니다.(http://www.uxora.com/unix/shell-script/20-getopts-with-man-page-and-long-options) 참조)

X( 옵션을 은 지원합니다: 기본제공 OS X(BSD) getopt는옵지을지않원하지버 GNU 을다지원니합음다만은전션긴▁th.brew install gnu-getopt그러면 다음과 유사한 것이 있습니다.cp /usr/local/Cellar/gnu-getopt/1.1.6/bin/getopt /usr/local/bin/gnu-getopt.

언급URL : https://stackoverflow.com/questions/402377/using-getopts-to-process-long-and-short-command-line-options