옵션을 구문 분석하고 있지만 getopts
긴 옵션도 처리하고 싶습니다.
print-args ()
{
title="$1" ; shift
printf "\n%s\n" "${title}: \$@:"
for arg in "$@"; do
(( i = i + 1 ))
printf "%s |%s|\n" "${i}." "$arg"
done
}
getopts_test ()
{
aggr=()
for arg in "$@"; do
case $arg in
("--colour"|"--color") aggr+=( "-c" ) ;;
("--colour="*|"--color="*) aggr+=( "-c" "${arg#*=}" ) ;;
(*) aggr+=( "$arg" ) ;;
esac
done
print-args "print" "$@"
eval set -- "${aggr[@]}"
print-args "eval" "$@"
set -- "${aggr[@]}"
print-args "set" "$@"
local OPTIND OPTARG
local shortopts="C:"
while getopts "$shortopts" arg; do
case $arg in
("c") context="$OPTARG" ;;
(*) break ;;
esac
done
shift $(( OPTIND - 1 ))
}
그런데 의 사용이 set -- "${aggr[@]}"
맞는지 궁금합니다.
아니면 다음 ( 을 사용하여 eval
)이 더 적합합니까?
eval set -- "${aggr[@]}"
아래에 표시된 테스트를 수행했습니다. eval을 사용하면 문자열 "Gunga Din"이 분할되는 반면 eval을 사용하면 set -- "${aggr[@]}"
단일 문자열로 올바르게 구문 분석됩니다.
getopts_test -f -g 130 --colour="170 20" "Gunga Din"
print: $@:
1. |-f|
2. |-g|
3. |130|
4. |--colour=170 20|
5. |Gunga Din|
eval: $@:
1. |-f|
2. |-g|
3. |130|
4. |-c|
5. |170|
6. |20|
7. |Gunga|
8. |Din|
set: $@:
1. |-f|
2. |-g|
3. |130|
4. |-c|
5. |170 20|
6. |Gunga Din|
그런 다음 GNU가 아닌 getopt
.
getopt_test ()
{
shortopts="Vuhv::H::w::e::n::l::C:"
shortopts="${shortopts}bgcrmo"
longopts="version,usage,help,verbosity::"
longopts="${longopts},heading::,warning::,error::"
longopts="${longopts},blu,grn,cyn,red,mgn,org"
opts=$( getopt -o "$shortopts" -l "$longopts" -n "${0##*/}" -- "$@" )
print-args "\$@:" "$@"
print-args "opts:" "$opts"
set -- "$opts"
print-args "set -- \"$opts\"" "$@"
eval set -- "$opts"
print-args "eval set -- \"$opts\"" "$@"
}
이로 인해 다음과 같은 결과가 발생했습니다.
getopt_test --warning=3 "foo'bar" "Gunga Din"
$@:
1. |--warning=3|
2. |foo'bar|
3. |Gunga Din|
opts:
1. | --warning '3' -- 'foo'\''bar' 'Gunga Din'|
set -- "$opts"
1. | --warning '3' -- 'foo'\''bar' 'Gunga Din'|
eval set -- "$opts"
1. |--warning|
2. |3|
3. |--|
4. |foo'bar|
5. |Gunga Din|
표시된 대로 getopt의 결과는 위치 인수가 재배열된 단일 항목입니다. 이는 옵션 구문 분석 및 처리를 위해 문자열 eval set -- "$opts"
의 위치 인수를 opts
5개 항목으로 분할하는 데 사용해야 함을 보여줍니다 .
답변1
--context
인수를 전처리하고 각 인수를 -C
변경 하여 getopts
처리할 수 있다는 아이디어가 있습니까 ? 나는 그것이 작동할 것이라고 생각하지만, GNU 스타일의 긴 옵션도 형식의 인수를 취할 수 --context=foobar
있으며 여기의 구성은 이를 지원하지 않는다는 점에 유의하십시오. 사용자는 여기에서 이 특정 도구에 두 개의 서로 다른 인수가 필요하다는 것을 알아야 합니다 --context
foobar
. 아니면 전처리를 더 복잡하게 만들어야 합니다.
로 시작하는 모든 인수를 확인하고 싶을 수도 있습니다. --
그렇지 않으면 잘못 입력한 인수는 그대로 --cotnext
적용되어 알 수 없는 옵션에 대한 불만 사항이 표시됩니다. getopts
(또는 더 나쁜 경우 잘못된 옵션이 활성화됩니다.)
그런데 의 사용이
set -- "${aggr[@]}"
맞는지 궁금합니다.아니면 다음(eval 사용)이 더 적합합니까?
set -- "${aggr[@]}"
배열의 요소를 고유한 단어로 확장한 다음 해당 단어를 위치 매개변수에 할당합니다. 각 배열 요소는 변경 없이 정확히 하나의 위치 매개변수가 됩니다.
eval set -- "${aggr[@]}"
배열의 모든 요소를 확장한 다음 공백으로 결합하고 앞에 추가한 set --
다음 결과를 쉘 명령으로 평가합니다. 즉, 배열 요소 abc def
, $(date >&2)
, 가 있는 경우 ghi'jkl
명령은 다음과 같습니다.
set -- abc def $(date >&2) ghi'jkl
abc
이는 두 개의 개별 매개변수로 및 로 끝나고 def
단일 작은따옴표로 인해 구문 오류가 발생한다는 점을 제외하고 날짜를 stderr에 인쇄합니다.
eval
쉘 입력에 대해 인용된 출력을 생성하도록 설계된 것이 있는 경우 사용하는 것이 적절할 것입니다.
Linux를 사용하고 있고 이식성에 관심이 없다면 roaima가 의견에서 제안한 것을 수행하고 util-linux 버전 getopt
( s
)을 사용할 수 있습니다. 긴 옵션도 지원합니다. 사용 방법을 보여주는 답변이 있습니다.getopt, getopts 또는 수동 구문 분석 - 짧은 옵션과 긴 옵션을 모두 지원하려면 무엇을 사용해야 합니까?그리고이 SO 답변그리고 또한내 대답은 여기.
그런데 그걸로 getopt
당신은~일 것이다명령 eval
으로서는 배열과 같은 목록이 아닌 단일 문자열을 출력으로 생성하는 것으로 제한되므로 셸 인용을 사용하여 문제를 해결합니다.
답변2
optstring에 인수를 사용하는 짧은 옵션을 추가하고 에서 실제 긴 옵션을 검색하여 내장된 --foo
스타일의 긴 옵션을 구문 분석할 수 있습니다 . 간단한 예:getopts
-
$OPTARG
while getopts :sc:-: o; do
case $o in
:) echo >&2 "option -$OPTARG needs an argument"; continue;;
'?') echo >&2 "unknown option -$OPTARG"; continue;;
-) o=${OPTARG%%=*}; OPTARG=${OPTARG#"$o"}; OPTARG=${OPTARG#=};;
esac
echo "OPT $o=$OPTARG"
done
shift "$((OPTIND - 1))"
echo "ARGS $*"
그런 다음 또는 로 사용할 수 script -c foo
있습니다 script --context=foo
.
짧은 옵션과 마찬가지로 긴 옵션도 검증하고 축약된 형식도 허용하려면 좀 더 복잡한 것이 필요합니다. 그런 형편없는 쉘 스크립트를 과도하게 엔지니어링하는 것은 별로 지혜롭지 않지만, 예를 원한다면 다음과 같습니다:
short_opts=sc:
long_opts=silent/ch/context:/check/co # those who take an arg END with :
# override via command line for testing purposes
# if [ "$#" -ge 2 ]; then
# short_opts=$1; long_opts=$2; shift 2
# fi
while getopts ":$short_opts-:" o; do
case $o in
:) echo >&2 "option -$OPTARG needs an argument" ;continue;;
'?') echo >&2 "bad option -$OPTARG" ;continue;;
-) o=${OPTARG%%=*}; OPTARG=${OPTARG#"$o"}; lo=/$long_opts/
case $lo in
*"/$o"[!/:]*"/$o"[!/:]*) echo >&2 "ambiguous option --$o"; continue;;
*"/$o"[:/]*) ;;
*) o=$o${lo#*"/$o"}; o=${o%%[/:]*} ;;
esac
case $lo in
*"/$o/"*) OPTARG= ;;
*"/$o:/"*)
case $OPTARG in
'='*) OPTARG=${OPTARG#=};;
*) eval "OPTARG=\$$OPTIND"
if [ "$OPTIND" -le "$#" ] && [ "$OPTARG" != -- ]; then
OPTIND=$((OPTIND + 1))
else
echo >&2 "option --$o needs an argument"; continue
fi;;
esac;;
*) echo >&2 "unknown option --$o"; continue;;
esac
esac
echo "OPT $o=$OPTARG"
done
shift "$((OPTIND - 1))"
echo "ARGS $*"
그 다음에
$ ./script --context=33
OPT context=33
$ ./script --con=33
OPT context=33
$ ./script --co
OPT co=
$ ./script --context
option --context needs an argument