태그 보관물: bash

bash

getopts를 사용하여 길고 짧은 명령 행 옵션 처리

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

나는 그것이 getopts사용될 수 있다는 것을 알고 있지만 Perl과 마찬가지로 쉘로도 똑같이 할 수 없었습니다.

이 작업을 수행하는 방법에 대한 아이디어는 다음과 같은 옵션을 사용할 수 있습니다.

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

위의 두 명령 모두 내 쉘과 동일한 것을 의미하지만을 사용 getopts하면이를 구현할 수 없습니까?



답변

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

  • 배쉬 내장 getopts. 이중 대시 접두사가있는 긴 옵션 이름은 지원하지 않습니다. 단일 문자 옵션 만 지원합니다.

  • 독립형 getopt명령 의 BSD UNIX 구현 (MacOS가 사용하는 것). 긴 옵션도 지원하지 않습니다.

  • 독립형 GNU 구현 getopt. GNU getopt(3)( getopt(1)Linux 의 명령 행 에서 사용)는 긴 옵션 구문 분석을 지원합니다.


다른 답변 getopts은 긴 옵션을 모방하기 위해 bash 내장 을 사용하는 솔루션을 보여줍니다 . 이 솔루션은 실제로 문자가 “-“인 짧은 옵션을 만듭니다. 따라서 플래그로 “-“를 얻습니다. 그런 다음 그 뒤에 OPTARG가되고 중첩으로 OPTARG를 테스트합니다 case.

이것은 영리하지만주의 사항이 있습니다.

  • getoptsopt 사양을 시행 할 수 없습니다. 사용자가 잘못된 옵션을 제공하면 오류를 반환 할 수 없습니다. OPTARG를 구문 분석 할 때 고유 한 오류 점검을 수행해야합니다.
  • OPTARG는 긴 옵션 이름에 사용되므로 긴 옵션 자체에 인수가있는 경우 사용법이 복잡해집니다. 추가 사례로 직접 코딩해야합니다.

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


답변

getopt그리고 getopts다른 짐승이 있고, 사람들은 무슨 오해의 조금을 갖고있는 것 같다. getoptsbash명령 행 옵션을 루프로 처리하고 찾은 각 옵션과 값을 내장 변수에 차례로 지정하여 추가로 처리 할 수있는 내장 명령입니다. getopt그러나 외부 유틸리티 프로그램이며 bash , Perl 모듈 또는 Python / 모듈 과 같은 옵션을 실제로 처리하지는 않습니다 . 이 모든 수행은 규범화에게 전달되는 옵션입니다 – 즉, 그것이 그들을 처리하는 쉘 스크립트 쉽게 그래서, 더 표준 형태로 변환. 예를 들어의 응용 프로그램은 다음을 변환 할 수 있습니다.getoptsGetoptoptparseargparsegetoptgetopt

myscript -ab infile.txt -ooutfile.txt

이것으로 :

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

실제 처리를 직접 수행해야합니다. getopt옵션을 지정할 수있는 방법에 대해 다양한 제한을 설정 한 경우 전혀 사용할 필요가 없습니다 .

  • 인수 당 하나의 옵션 만 넣으십시오.
  • 모든 옵션은 위치 매개 변수 (즉, 옵션이 아닌 인수)보다 우선합니다.
  • 값이있는 옵션 (예 : -o위)의 경우 값은 공백 뒤에 별도의 인수로 가야합니다.

getopt대신에 사용 getopts합니까? 기본적인 이유는 GNU만이 getopt긴 이름의 명령 줄 옵션을 지원하기 때문입니다 . 1 ( getoptLinux에서는 GNU 가 기본값입니다. Mac OS X 및 FreeBSD는 기본적이고 유용 getopt하지는 않지만 GNU 버전을 설치할 수 있습니다 (아래 참조).

예를 들어, 다음 getopt은 내 스크립트에서 GNU를 사용하는 예입니다 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옵션을 --verbose -d -m 4096 --minheap 20 --maxheap 40 --debugfile "/Users/John Johnson/debug.txt"보다 쉽게 ​​처리 할 수 ​​있도록 옵션을 정규화하는 것입니다. 주위에 인용 "$1"하고 "$2"그들에 공백이 제대로 처리 얻을과 그 인수를 보장하므로 중요하다.

처음 9 줄을 삭제하면 (모든 eval set줄 을 통해 ) 코드가 계속 작동합니다 ! 그러나 코드는 어떤 종류의 옵션을 받아 들일지 결정하기가 훨씬 쉽습니다. 특히, 위에서 설명한 “정식”형식으로 모든 옵션을 지정해야합니다. 의 사용으로 getopt, 그러나, 당신은 그룹의 단일 문자 옵션은 긴 옵션을 사용하여 하나의 짧은 비 모호한 형태로 사용할 수 있습니다 --file foo.txt또는 --file=foo.txt중 스타일, 사용 -m 4096또는 -m4096스타일, 임의의 순서로 옵션이 아닌 옵션을 혼합 등 getopt인식 할 수 없거나 모호한 옵션이있는 경우 오류 메시지를 출력합니다.

참고 : 실제로 기능과 호출 규칙이 서로 다른 완전히 다른getopt 기본 버전 getopt과 GNU 버전이 getopt있습니다. 2 기본 getopt은 상당히 깨졌습니다 : 긴 옵션을 처리 할뿐만 아니라 인수 또는 빈 인수 내부에 포함 된 공백도 처리 할 수 ​​없지만 getopts올바른 작업을 수행합니다. 위 코드는 기본적으로 작동하지 않습니다 getopt. GNU getopt는 기본적으로 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. FreeBSD에서 설치하십시오 misc/getopt.

자신의 프로그램에 대한 예제 코드를 수정하기위한 빠른 안내서 : 처음 몇 줄 중에서 모두 “보일러 판”은 호출하는 줄을 제외하고 동일하게 유지되어야합니다 getopt. 뒤에 프로그램 이름을 변경하고 뒤에 -n짧은 옵션을 지정 -o하고 뒤에 긴 옵션을 지정해야 --long합니다. 값을 취하는 옵션 뒤에 콜론을 넣으십시오.

마지막으로, set대신에 코드가 있으면 eval setBSD 용으로 작성된 것입니다 getopt. eval set두 가지 버전 모두에서 잘 작동 하는 스타일 을 사용하도록 변경해야 getopt하지만 일반 set은 GNU에서 제대로 작동하지 않습니다 getopt.

1 실제로 는 긴 이름의 옵션 getoptsksh93지원하지만이 쉘은 자주 사용되지 않습니다 bash. 에서 zsh사용 zparseopts이 기능을 얻을 수 있습니다.

2 기술적으로 “GNU getopt“는 잘못된 이름입니다. 이 버전은 실제로 GNU 프로젝트가 아닌 Linux 용으로 작성되었습니다. 그러나 모든 GNU 규칙을 따르며 getopt” GNU ” 라는 용어 가 일반적으로 사용됩니다 (예 : FreeBSD에서).


답변

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는 긴 옵션에 대해 검사 또는 옵션 인수 구문 분석을 수행 하지 않습니다. 위의 스크립트 조각은이를 수동으로 수행하는 방법을 보여줍니다. 기본 원칙은 데비안 Almquist 쉘 ( “대시”)에서도 작동합니다. 특수한 경우를 참고하십시오.

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

http://mywiki.wooledge.org/BashFAQ 에서 GreyCat이 처음부터 지적했듯이이 트릭은 옵션 인수를 허용하는 쉘의 비표준 동작 (예 : “-f filename”의 파일 이름)을 이용합니다. “-ffilename”에서와 같이 옵션에 연결됩니다. POSIX의 “- longoption”옵션 구문 분석을 종료 할 비 옵션 인수에 모든 longoptions를 켜 표준의 경우 그 사이에 공간이 있어야합니다 말한다.


답변

내장 getopts명령은 여전히 ​​AFAIK이며 단일 문자 옵션으로 만 제한됩니다.

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단일 문자 옵션 만 처리한다는 사실에 의해 제한 되기는하지만 내장 기능 이 우수한 이유 입니다.


답변

긴 옵션과 함께 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


답변

표준 getopts내장에서는 -“옵션” 의 ” 인수”로 긴 옵션을 구문 분석 할 수 있습니다.

이것은 이식 가능한 기본 POSIX 셸이므로 외부 프로그램이나 bashism이 필요하지 않습니다.

받는 인수로이 가이드 구현 긴 옵션 -옵션은, 그래서 --alpha으로 볼 getopts-인수 alpha--bravo=foo같이 보인다 -인수 bravo=foo. 간단한 인수로 진정한 인수를 얻을 수 있습니다 ${OPTARG#*=}.

이 예에서 -b-c(및 긴 형식 --bravo--charlie)에는 필수 인수가 있습니다. 긴 옵션에 대한 인수는 등호 뒤에옵니다 --bravo=foo( 예 : 긴 옵션에 대한 공백 구분 기호는 구현하기 어렵습니다 (아래 참조)).

이것은 사용하기 때문에 getopts내장을 , 같은이 솔루션 지원 사용 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

옵션이 대시 ( -)이면 긴 옵션입니다. getopts실제 long 옵션 ( $OPTARG예 : --bravo=foo원래 sets OPT='-'및) 을 구문 분석했습니다 OPTARG='bravo=foo'. if스 D 자 세트 $OPT의 내용에 $OPTARG최초의 등호 기호 전에 ( bravo다음 예에서)과의 처음부터 것을 제거 $OPTARG(항복하지 =foo아니가있는 경우이 단계, 또는 빈 문자열 =). 마지막으로, 논증의 선두를 제거합니다 =. 이 시점에서 $OPT짧은 옵션 (한 문자) 또는 긴 옵션 (2 자 이상)입니다.

그런 case다음 짧거나 긴 옵션과 일치합니다. 짧은 옵션의 경우 getopts옵션과 누락 된 인수에 대해 자동으로 불평하므로 비어 needs_arg있을 때 치명적으로 종료되는 함수를 사용하여 수동으로 복제해야합니다 $OPTARG. ??*조건은 남아있는 긴 옵션 (일치 ?하나의 문자와 일치와 *일치하는 0 개 이상의, 그래서 ??*우리가 종료하기 전에 “잘못된 옵션”오류를 발행 할 수 있도록 2 + 문자와 일치).

(대문자 변수 이름에 대한 참고 사항 : 일반적으로 시스템 사용을 위해 모든 대문자 변수를 예약하는 것이 좋습니다. 나는 $OPT이를 대문자로 유지하기 위해이를 대문자로 유지하고 $OPTARG있지만,이 규칙을 위반합니다. 이것은 시스템이 수행해야 할 일이기 때문에 적합하며 그러한 변수를 사용하는 표준 (afaik)이 없기 때문에 안전해야합니다.)


긴 옵션에 대한 예기치 않은 인수에 대해 불평하려면 필수 인수에 대해 수행 한 작업을 모방하십시오. 도우미 함수를 사용하십시오. 예상치 못한 인수에 대해 불평하기 위해 테스트를 뒤집기 만하면됩니다.

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

이 답변이전 버전은 공백으로 구분 된 인수로 긴 옵션을 수락하려고 시도했지만 신뢰할 수 없었습니다. getopts인수가 범위를 벗어 $OPTIND났고 모든 쉘에서 수동 증분 이 작동하지 않는다는 가정하에 조기 종료 할 수 있습니다.

이것은 다음 기술 중 하나를 사용하여 수행됩니다.

그리고 다음과 같이 결론지었습니다. [ $# -gt $OPTIND ] && OPTIND=$((OPTIND+1))


답변

이식 가능한 쉘 라이브러리 인 shFlags 를 살펴보십시오 (즉, Linux, Solaris 등의 경우 sh, bash, dash, ksh, zsh).

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

다음은 shFlag를Hello, world! 사용한 간단한 것입니다. .

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

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

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

나머지는 짧은 옵션을 사용해야합니다.

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

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