Bash getoptsで-temp
オプションを解析しようとしています。次のようにスクリプトを呼び出しています。
./myscript -temp /foo/bar/someFile
以下は、オプションを解析するために使用しているコードです。
while getopts "temp:shots:o:" option; do
case $option in
temp) TMPDIR="$OPTARG" ;;
shots) NUMSHOTS="$OPTARG" ;;
o) OUTFILE="$OPTARG" ;;
*) usage ;;
esac
done
shift $(($OPTIND - 1))
[ $# -lt 1 ] && usage
getopts
は短いオプションのみを解析できます。
ほとんどのシステムには外部getopt
コマンドもありますが、getoptは標準ではなく、すべての引数(空白と空の引数を持つ引数)を安全に処理できないため、GNU getoptはそれらを安全に処理できますが、GNU固有の方法で使用する場合のみです。
より簡単な選択は、どちらも使用せず、スクリプトの引数をwhileループで繰り返し、自分で解析を行うことです。
例については、 http://mywiki.wooledge.org/BashFAQ/035 を参照してください。
他の人が説明したように、getoptsは長いオプションを解析しません。 getoptを使用できますが、ポータブルではありません(プラットフォームによっては壊れています...)
回避策として、シェルループを実装できます。ここで、標準のgetoptsコマンドを使用する前に長いオプションを短いオプションに変換する例(私の意見では簡単です):
# Transform long options to short ones
for arg in "$@"; do
shift
case "$arg" in
"--help") set -- "$@" "-h" ;;
"--rest") set -- "$@" "-r" ;;
"--ws") set -- "$@" "-w" ;;
*) set -- "$@" "$arg"
esac
done
# Default behavior
rest=false; ws=false
# Parse short options
OPTIND=1
while getopts "hrw" opt
do
case "$opt" in
"h") print_usage; exit 0 ;;
"r") rest=true ;;
"w") ws=true ;;
"?") print_usage >&2; exit 1 ;;
esac
done
shift $(expr $OPTIND - 1) # remove options from positional parameters
getopts
は、シェルプロシージャで1文字の位置パラメータのみを解析するために使用されます(GNUスタイルの長いオプション(--myoption)またはXF86スタイルの長いオプション(-myoption)はありません)
これを達成する最も簡単な方法は、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
これが方法です-シェルスクリプトを呼び出します
myscript.sh --input1 "ABC" --input2 "PQR" --input2 "XYZ"
この質問は2年以上前に投稿されましたが、XFree86スタイルの長いオプションのサポートも必要だと感じました。また、getoptsからできることを取得したかったのです。 GCCスイッチ-rdynamic
を検討してください。 r
をフラグ文字としてマークし、$OPTARG
...内でdynamic
を期待していますが、-r dynamic
を拒否し、r
に続く他のオプションを受け入れたい。
以下に示した考え方は、$OPTIND
がフラグの後にスペース(ギャップ)がある場合、それ以外の場合よりも1つ大きくなるという観察に基づいています。そこで、$OPTIND
と呼ばれる$PREVOPTIND
の以前の値を保持するbash変数を定義し、while
ループの終わりに更新します。 $OPTIND
が1
が$PREVOPTIND
より大きい場合、ギャップはありません(つまり、-rdynamic
); $GAP
はfalse
に設定されます。代わりに$OPTIND
が2
より大きい$PREVOPTIND
より大きい場合、doにギャップがあり(たとえば-r dynamic
)、$GAP
が設定されますtrue
に。
usage() { echo usage: error from $1; exit -1; }
OPTIND=1
PREVOPTIND=$OPTIND
while getopts "t:s:o:" option; do
GAP=$((OPTIND-(PREVOPTIND+1)))
case $option in
t) case "${OPTARG}" in
emp) # i.e. -temp
((GAP)) && usage "-${option} and ${OPTARG}"
TMPDIR="$OPTARG"
;;
*)
true
;;
esac
;;
s) case "${OPTARG}" in
hots) # i.e. -shots
((GAP)) && usage
NUMSHOTS="$OPTARG"
;;
*) usage "-${option} and ${OPTARG}" ;;
esac
;;
o) OUTFILE="$OPTARG" ;;
*) usage "-${option} and ${OPTARG}" ;;
esac
PREVOPTIND=$OPTIND
done
shift $(($OPTIND - 1))
builtin bash getopts
は短いオプションのみを解析しますが、getoptsが長いオプションを処理するようにスクリプトの数行を追加できます。
http://www.uxora.com/unix/Shell-script/22-handle-long-options-with-getopts にあるコードの一部を次に示します。
#== set options ==#
SCRIPT_OPTS=':fbF:B:-:h'
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
それ以外の場合、最近のKornシェルksh93では、getopts
は長いオプションを自然に解析し、manページを表示することさえできます。 ( http://www.uxora.com/unix/Shell-script/20-getopts-with-man-page-and-long-options を参照)
Michel VONGVILAY。
getoptsBashビルトインを長いオプションに使用することはできません。少なくとも、独自の解析関数を構築する必要はありません。代わりに/ usr/bin/getoptバイナリ(システム上で til-linux パッケージ;マイレージは異なる場合があります)。
特定の呼び出しオプションについては、getopt(1)を参照してください。
@mcooliveに感謝します。
あなたの$ @アイデアを使って、Word全体と長いオプションを1文字のオプションに変換することができました。このアイデアを使用している人に、getoptsループで引数を実行する前にshift $(expr $OPTIND - 1)
も含める必要があることに注意してください。
まったく異なる目的ですが、これはうまく機能しています。
# convert long Word options to short Word for ease of use and portability
for argu in "$@"; do
shift
#echo "curr arg = $1"
case "$argu" in
"-start"|"--start")
# param=param because no arg is required
set -- "$@" "-s"
;;
"-pb"|"--pb"|"-personalbrokers"|"--personalbrokers")
# pb +arg required
set -- "$@" "-p $1"; #echo "arg=$argu"
;;
"-stop"|"--stop")
# param=param because no arg is required
set -- "$@" "-S"
;;
# the catch all option here removes all - symbols from an
# argument. if an option is attempted to be passed that is
# invalid, getopts knows what to do...
*) [[ $(echo $argu | grep -E "^-") ]] && set -- "$@" "${argu//-/}" || echo "no - symbol. not touching $argu" &>/dev/null
;;
esac
done
#echo -e "\n final option conversions = $@\n"
# remove options from positional parameters for getopts parsing
shift $(expr $OPTIND - 1)
declare -i runscript=0
# only p requires an argument hence the p:
while getopts "sSp:" param; do
[[ "$param" == "p" ]] && [[ $(echo $OPTARG | grep -E "^-") ]] && funcUsage "order"
#echo $param
#echo "OPTIND=$OPTIND"
case $param in
s)
OPTARG=${OPTARG/ /}
getoptsRan=1
echo "$param was passed and this is it's arg $OPTARG"
arg0=start
;;
p)
OPTARG=${OPTARG/ /}
getoptsRan=1
echo "$param was passed and this is it's arg $OPTARG"
[[ "$OPTARG" == "all" ]] && echo -e "argument \"$OPTARG\" accepted. continuing." && (( runscript += 1 )) || usage="open"
[[ $( echo $pbString | grep -w "$OPTARG" ) ]] && echo -e "pb $OPTARG was validated. continuing.\n" && (( runscript += 1 )) || usage="personal"
[[ "$runscript" -lt "1" ]] && funcUsage "$usage" "$OPTARG"
arg0=start
;;
S)
OPTARG=${OPTARG/ /}
getoptsRan=1
echo "$param was passed and this is it's arg $OPTARG"
arg0=stop
;;
*)
getoptsRan=1
funcUsage
echo -e "Invalid argument\n"
;;
esac
done
funcBuildExcludes "$@"
shift $((OPTIND-1))
ビルトインgetopts
の使用に問題が発生した場合の簡単なDIY:
つかいます:
$ ./test-args.sh --a1 a1 --a2 "a 2" --a3 --a4= --a5=a5 --a6="a 6"
a1 = "a1"
a2 = "a 2"
a3 = "TRUE"
a4 = ""
a5 = "a5"
a6 = "a 6"
a7 = ""
スクリプト:
#!/bin/bash
function main() {
ARGS=`getArgs "$@"`
a1=`echo "$ARGS" | getNamedArg a1`
a2=`echo "$ARGS" | getNamedArg a2`
a3=`echo "$ARGS" | getNamedArg a3`
a4=`echo "$ARGS" | getNamedArg a4`
a5=`echo "$ARGS" | getNamedArg a5`
a6=`echo "$ARGS" | getNamedArg a6`
a7=`echo "$ARGS" | getNamedArg a7`
echo "a1 = \"$a1\""
echo "a2 = \"$a2\""
echo "a3 = \"$a3\""
echo "a4 = \"$a4\""
echo "a5 = \"$a5\""
echo "a6 = \"$a6\""
echo "a7 = \"$a7\""
exit 0
}
function getArgs() {
for arg in "$@"; do
echo "$arg"
done
}
function getNamedArg() {
ARG_NAME=$1
sed --regexp-extended --quiet --expression="
s/^--$ARG_NAME=(.*)\$/\1/p # Get arguments in format '--arg=value': [s]ubstitute '--arg=value' by 'value', and [p]rint
/^--$ARG_NAME\$/ { # Get arguments in format '--arg value' ou '--arg'
n # - [n]ext, because in this format, if value exists, it will be the next argument
/^--/! p # - If next doesn't starts with '--', it is the value of the actual argument
/^--/ { # - If next do starts with '--', it is the next argument and the actual argument is a boolean one
# Then just repla[c]ed by TRUE
c TRUE
}
}
"
}
main "$@"