나는 getopt/getopts보다 쉘 스크립트 인수를 처리하는 더 깨끗하고 "자체 문서화"되는 방법을 찾고 있습니다.
그것은 제공해야합니다 ...
- 값 뒤에 "=" 또는 " "(공백)이 있거나 없는 긴 옵션이 완전히 지원됩니다.
- 하이픈으로 연결된 옵션 이름을 올바르게 처리합니다(예: --ignore-case).
- 인용된 옵션 값을 올바르게 처리합니다(예: --text "텍스트 문자열").
getopt/getopts에 필요한 케이스 문에 포함된 큰 루프의 오버헤드를 제거하고 옵션 처리를 다음과 같이 줄이고 싶습니다.
option=argumentparse "$@"
[[ option == "" ]] && helpShow
[[ option =~ -h|--help ]] && helpShow
[[ option =~ -v|--version ]] && versionShow
[[ option =~ -G|--GUI ]] && GUI=$TRUE
[[ option =~ --title ]] && TITLE=${option["--title"]}
여기서 인수parse() 함수는 다양한 구문 가능성을 일관된 형식, 가능한 연관 배열로 구문 분석합니다.
어딘가에 인코딩된 것이 있을 것입니다. 어떤 아이디어가 있나요?
(업데이트 및 이름 변경)
답변1
이 질문은 (적어도 나에게는) 꽤 많이 보았지만 답변이 제출되지 않았기 때문에 채택된 솔루션을 전달합니다...
노트
다중 인터페이스 출력 함수와 같은 일부 함수가 ifHelpShow()
사용 uiShow()
되지만 해당 호출에는 관련 정보가 포함되어 있지만 구현에는 포함되어 있지 않기 때문에 여기에 포함되지 않습니다.
###############################################################################
# FUNCTIONS (bash 4.1.0)
###############################################################################
function isOption () {
# isOption "$@"
# Return true (0) if argument has 1 or more leading hyphens.
# Example:
# isOption "$@" && ...
# Note:
# Cannot use ifHelpShow() here since cannot distinguish 'isOption --help'
# from 'isOption "$@"' where first argument in "$@" is '--help'
# Revised:
# 20140117 docsalvage
#
# support both short and long options
[[ "${1:0:1}" == "-" ]] && return 0
return 1
}
function optionArg () {
ifHelpShow "$1" 'optionArg --option "$@"
Echo argument to option if any. Within "$@", option and argument may be separated
by space or "=". Quoted strings are preserved. If no argument, nothing echoed.
Return true (0) if option is in argument list, whether an option-argument supplied
or not. Return false (1) if option not in argument list. See also option().
Examples:
FILE=$(optionArg --file "$1")
if $(optionArg -f "$@"); then ...
optionArg --file "$@" && ...
Revised:
20140117 docsalvage' && return
#
# --option to find (without '=argument' if any)
local FINDOPT="$1"; shift
local OPTION=""
local ARG=
local o=
local re="^$FINDOPT="
#
# echo "option start: FINDOPT=$FINDOPT, o=$o, OPTION=$OPTION, ARG=$ARG, @=$@" >&2
#
# let "$@" split commandline, respecting quoted strings
for o in "$@"
do
# echo "FINDOPT=$FINDOPT, o=$o, OPTION=$OPTION, ARG=$ARG" >&2
# echo " o=$o" >&2
# echo "re=$re" >&2
#
# detect --option and handle --option=argument
[[ $o =~ $re ]] && { OPTION=$FINDOPT; ARG="${o/$FINDOPT=/}"; break; }
#
# $OPTION will be non-null if --option was detected in last pass through loop
[[ ! $OPTION ]] && [[ "$o" != $FINDOPT ]] && { continue; } # is a positional arg (no previous --option)
[[ ! $OPTION ]] && [[ "$o" == $FINDOPT ]] && { OPTION="$o"; continue; } # is the arg to last --option
[[ $OPTION ]] && isOption "$o" && { break; } # no more arguments
[[ $OPTION ]] && ! isOption "$o" && { ARG="$o"; break; } # only allow 1 argument
done
#
# echo "option final: FINDOPT=$FINDOPT, o=$o, OPTION=$OPTION, ARG=$ARG, @=$@" >&2
#
# use '-n' to remove any blank lines
echo -n "$ARG"
[[ "$OPTION" == "$FINDOPT" ]] && return 0
return 1
}
###############################################################################
# MAIN (bash 4.1.0) (excerpt of relevant lines)
###############################################################################
# options
[[ "$@" == "" ]] && { zimdialog --help ; exit 0; }
[[ "$1" == "--help" ]] && { zimdialog --help ; exit 0; }
[[ "$1" == "--version" ]] && { uiShow "version $VERSION\n"; exit 0; }
# options with arguments
TITLE="$(optionArg --title "$@")"
TIP="$( optionArg --tip "$@")"
FILE="$( optionArg --file "$@")"
답변2
여기에 좋은 답변이 있지만 OP가 명시적으로 요청했습니다.더 간단하다명령줄 옵션을 처리합니다. getopt
from을 사용하는 것보다 복잡한 쉘 옵션을 구문 분석하는 더 쉬운 방법 은 없다고 생각합니다.유틸리티Linux:
$ cat opts.sh
#!/bin/bash
# Print usage and exit
usage() {
exec >&2 # Write everything to STDERR (consistent with getopt errors)
(($#)) && echo "$@"
echo "Usage: $0 [opts...] [args...]"
exit 1
}
# Use getopt to validate options and reorder them. On error print usage
OPTS=$(getopt -s bash -o 'ab:c' -l 'longopt,longopt2,longwitharg:' -- "$@") || usage
# Replace our arguments with the reordered version
eval set -- "$OPTS"
# At this point everything up to "--" is options
declare opt_a opt_c longopt longopt2 longwitharg
declare -a opt_b # Array to accumulate -b arguments
while (($#))
do
case $1 in
-a) opt_a=1;;
-b) opt_b+=("$2"); shift;;
-c) ((++opt_c));;
--longopt) longopt=1;;
--longopt2) ((++longopt2));;
--longwitharg) longwitharg=$2; shift;;
--) shift; break;; # We're done with options, shift over "--" and move on...
*) usage "Unknown argument: $1" # Should not happen unless getopt errors are ignored.
esac
# Always shift once (for options with arguments we already shifted in the case so it's the 2nd shift)
shift
done
echo "Remaining arguments after parsing options: $#"
# Now you can work directly with "$@" or slurp it in an array
args=("$@")
# Here's what we're left with:
declare -p opt_a opt_b opt_c longopt longopt2 longwitharg
# This is how you iterate over an array which may contain spaces and other field separators
for file in "${args[@]}"
do
echo "File arg: $file"
done
getopt
유효성 검사를 수행하고 오류를 반환합니다(하지 않도록 지시하지 않는 한, 이 경우 직접 오류를 잡을 수 있습니다). 예를 들어:
$ ./opts.sh --badopt
getopt: unrecognized option '--badopt'
다른 모든 항목은 올바르게 정렬되고 참조됩니다.
$ ./opts.sh -ab3 -b8 file1 -ccc --longopt --longwitharg=abc --longwitharg "abc def" "file with spaces"
Remaining arguments after parsing options: 2
declare -- opt_a="1"
declare -a opt_b=([0]="3" [1]="8")
declare -- opt_c="3"
declare -- longopt="1"
declare -- longopt2
declare -- longwitharg="abc def"
File arg: file1
File arg: file with spaces
여기서의 주장은 다음과 같습니다.
-a
플래그입니다. 존재하는 경우 1로 설정됩니다.-b
반복합니다. 각 인스턴스가opt_b
배열 에 추가됩니다.-c
3의 발생 횟수를 계산하는 카운터입니다.--longopt
이런 표시야-a
--longopt2
유사한 카운터입니다-c
(이 예에서는 사용되지 않았으므로 설정되지 않았습니다.산술 확장).--longwitharg
매개변수가 있는 일반 옵션입니다. 이 예에서는 나중에 명령줄에서 해당 값을 재정의합니다.file1
나머지 인수는 임의의 위치에 지정되어 있지만 getopt가 명령줄을 재정렬한file with spaces
후에 모두 끝에 표시됩니다 .--
변수를 인용하는 것은 공백이 있는 인수를 올바르게 처리하는 데 매우 중요합니다. 하지만 필요하지 않은 곳에는 인용문을 사용하는 것을 자발적으로 피합니다. 예를 들면 다음과 같습니다.
- 직접 변수 할당:
longwitharg=$2
- 스위치 박스:
case $1 in
확실하지 않은 경우 일반적으로 추가 인용문을 사용해도 문제가 되지 않습니다.