オプションを解析していますが、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[@]}"
以下のようにテストを行いました。文字列 "Gunga Din"はevalを使用すると分割されますが、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
ここではあなたの設定はサポートしていません。ここでは、この特定のツールに2つの異なるパラメータが必要であることを知っておく必要があります--context
foobar
。あるいは、前処理をより複雑にする必要があります。
で始まるすべてのパラメータを確認したい場合があります--
。それ以外の場合は、誤字が発生したままで不明なオプションに関する苦情が表示されます--cotnext
。getopts
(または悪い場合は無効なオプションが有効になります。)
ところで使い方が合っているか知りたいです
set -- "${aggr[@]}"
。それとも、次の(evalを使用)がより適切ですか?
set -- "${aggr[@]}"
配列の要素を別の単語に展開し、その単語を位置パラメータに割り当てます。各配列要素は正確に1つの位置パラメータになり、変更する必要はありません。
eval set -- "${aggr[@]}"
配列内のすべての要素は展開され、スペースで連結され、先頭に追加され、set --
結果はシェルコマンドによって評価されます。つまり、配列要素、、、abc def
が$(date >&2)
ある場合、ghi'jkl
コマンドは次のようになります。
set -- abc def $(date >&2) ghi'jkl
abc
これは、2つの異なる引数で終わり、単一def
引用符だけで構文エラーが発生することを除いて、日付をstderrに出力します。
eval
シェルへの入力として引用された出力を生成するように設計されたものがある場合は、それを使用することが適切です。
Linuxを使用していて移植性に興味がない場合は、roaimaが説明のように実行し、util-linuxバージョンgetopt
(s
)を使用できます。長いオプションもサポートされており、使用方法を示す答えがあります。getopt、getopts、または手動解析 - 短いオプションと長いオプションの両方をサポートするには何を使用する必要がありますか?そしてこの回答そして私の答えはここにあります。
さて、getopt
あなたは会議コマンドeval
として、配列などのリストではなく単一の文字列を出力として生成することに制限されているため、シェル引用符を使用して問題を解決します。
答え2
--foo
組み込み関数をgetopts
使用して、-
引数を短いオプションとして追加し、引数をoptstringに渡し、ここで実際の長いオプションを検索してスタイルの長いオプションを解析できます$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