シェルスクリプトを使用して、長い形式と短い形式のコマンドラインオプションを呼び出したい。
私はgetopts
が使えることを知っていますが、Perlのように、私はShellでも同じことができませんでした。
これをどのように行うことができるかについての任意のアイデア。私は以下のようなオプションを使用することができます。
./Shell.sh --copyfile abc.pl /tmp/
./Shell.sh -c abc.pl /tmp/
上で、両方のコマンドは私のシェルにとって同じことを意味します、しかしgetopts
を使って、私はこれらを実装することができませんでしたか?
考えられる3つの実装があります。
Bash組み込みgetopts
。これは、二重ダッシュ接頭辞を持つ長いオプション名をサポートしません。 1文字のオプションしかサポートされていません。
スタンドアロンのgetopt
コマンドのBSD UNIX実装(これはMacOSが使用するものです)。これも長いオプションをサポートしません。
スタンドアロンのgetopt
のGNU実装。 GNU getopt(3)
(コマンドラインで使用されます getopt(1)
(Linuxの場合)長いオプションの解析をサポートします。
他のいくつかの答えは、長いオプションを模倣するためにbash組み込みのgetopts
を使うための解決策を示しています。その解決策は実際には文字が " - "である短いオプションを作ります。だからフラグとして " - "を得ます。その後、それに続くものはすべてOPTARGになります。ネストしたcase
を使用してOPTARGをテストします。
これは巧妙ですが、注意が必要です。
getopts
はopt仕様を強制できません。ユーザーが無効なオプションを指定してもエラーを返すことはできません。 OPTARGを解析するときには、独自のエラーチェックをする必要があります。それで、長いオプションのサポートの欠如を回避するためにもっと多くのコードを書くことは可能ですが、これはもっと多くの仕事であり、コードを単純化するためにgetoptパーサーを使う目的を部分的に無効にします。
getopt
とgetopts
は異なる獣であり、人々は自分たちがしていることについて少し誤解しているようです。 getopts
は、bash
への組み込みコマンドで、ループ内のコマンドラインオプションを処理し、見つかった各オプションと値を組み込み変数に順番に割り当てるため、さらに処理できます。ただし、getopt
は外部ユーティリティプログラムであり、実際にあなたのオプションを処理しません bash getopts
、Perl Getopt
モジュール、またはPython optparse
/argparse
モジュールはそうします。 getopt
が行うことは、渡されるオプションを正規化することです。つまり、それらをより標準的な形式に変換し、シェルスクリプトで処理しやすくします。たとえば、getopt
のアプリケーションは、次を変換します。
myscript -ab infile.txt -ooutfile.txt
これに:
myscript -a -b -o outfile.txt infile.txt
実際の処理は自分で行う必要があります。オプションの指定方法にさまざまな制限を設けている場合は、getopt
を使用する必要はありません。
-o
)の場合、値は(スペースの後の)別の引数として指定する必要があります。なぜgetopt
ではなくgetopts
を使用するのですか?基本的な理由は、GNU getopt
のみが長い名前のコマンドラインオプションのサポートを提供するからです。1 (GNU getopt
はLinuxのデフォルトです。MacOS 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
行まですべて)を削除すると、コードはstill workになります!ただし、コードはどのような種類のオプションを受け入れるかについて非常に慎重になります。特に、上記の「標準的な」形式ですべてのオプションを指定する必要があります。ただし、getopt
を使用すると、1文字のオプションをグループ化し、長いあいまいでない短い形式を使用し、--file foo.txt
または--file=foo.txt
スタイルを使用し、-m 4096
または-m4096
スタイルを使用し、オプションと非オプションを混在させることができます任意の順序など。getopt
は、認識できないオプションやあいまいなオプションが見つかった場合にもエラーメッセージを出力します。
NOTE:実際には、getopt
には2つの完全に異なるバージョンがあります。基本的な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
の後に長いオプションを指定する必要があります。値を取るオプションの後にコロンを置きます。
最後に、eval set
の代わりにset
のみを含むコードが表示される場合、それはBSD getopt
向けに作成されています。 eval set
スタイルを使用するように変更する必要があります。これは、getopt
の両方のバージョンで正常に機能しますが、プレーンなset
はGNU getopt
では正しく機能しません。
1実際、ksh93
のgetopts
は長い名前のオプションをサポートしていますが、このシェルはbash
ほど頻繁には使用されません。 zsh
では、zparseopts
を使用してこの機能を取得します。
2技術的には、「GNU getopt
」は誤った名称です。このバージョンは、GNUプロジェクトではなく、実際にLinux用に作成されました。ただし、すべてのGNU規則に従い、「GNU getopt
」という用語が一般的に使用されます(例: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
現在の作業ディレクトリ の実行可能ファイルname = 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
チェックもオプション引数のパースも行いません。上記のスクリプトの断片は、これを手動で行う方法を示しています。基本原則はDebian Almquist Shell( "ダッシュ")でも機能します。特別な場合に注意してください。
getopts -- "-:" ## without the option terminator "-- " bash complains about "-:"
getopts "-:" ## this works in the Debian Almquist Shell ("dash")
GreyCatが http://mywiki.wooledge.org/BashFAQ で指摘しているように、このトリックは非標準の振る舞いを悪用することに注意してください。オプション引数(すなわち "-f filename"の中のファイル名)をオプションに連結することを許可するシェル( "-ffilename"のように)。 POSIX 標準では、それらの間にはスペースが必要です。これは、 " - longoption"の場合、オプションの解析を終了して有効になります。オプション以外の引数へのすべてのlongoptions。
組み込みのgetopts
コマンドは、やはり残念ながら、1文字のオプションに限定されています。
構文解析が容易になるようにオプションのセットを再編成する外部プログラム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
が優れている理由ですが、それが1文字のオプションしか扱えないという事実によって制限されます。
これは実際には長いオプションで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
組み込み関数によって-
“ option”への“引数”として解析できます。
これは移植可能なネイティブPOSIXシェルです。外部プログラムやbashismは必要ありません。
このガイドでは、長いオプションを-
オプションの引数として実装しているため、--alpha
はgetopts
によって-
と見なされ、引数alpha
および--bravo=foo
は引数-
を持つbravo=foo
として表示されます。真の引数は、単純な置換で取得できます:${OPTARG#*=}
。
この例では、-b
(およびその長い形式の--bravo
)には必須オプションがあります(長い形式の強制を手動で再構築することに注意してください)。長い引数に対する非ブールオプションは等号の後に来ます。 --bravo=foo
(長いオプションのスペース区切り文字は実装が困難です)。
これはgetopts
を使用するため、このソリューションはcmd -ac --bravo=foo -d FILE
(オプション-a
と-c
を組み合わせ、長いオプションを標準オプションとインターリーブ)などの使用をサポートします。苦労するか、失敗します。
while getopts ab:c-: arg; do
case $arg in
a ) ARG_A=true ;;
b ) ARG_B="$OPTARG" ;;
c ) ARG_C=true ;;
- ) LONG_OPTARG="${OPTARG#*=}"
case $OPTARG in
alpha ) ARG_A=true ;;
bravo=?* ) ARG_B="$LONG_OPTARG" ;;
bravo* ) echo "No arg for --$OPTARG option" >&2; exit 2 ;;
charlie ) ARG_C=true ;;
alpha* | charlie* )
echo "No arg allowed for --$OPTARG option" >&2; exit 2 ;;
'' ) break ;; # "--" terminates argument processing
* ) echo "Illegal option --$OPTARG" >&2; exit 2 ;;
esac ;;
\? ) exit 2 ;; # getopts already reported the illegal option
esac
done
shift $((OPTIND-1)) # remove parsed options and args from $@ list
引数がダッシュ(-
)の場合、さらに2つのコンポーネントがあります。フラグ名と(オプションで)引数です。これらのコマンドは、最初の等号(=
)でコマンドの標準的な方法で区切ります。したがって、$LONG_OPTARG
は、フラグ名または等号のない$OPTARG
のコンテンツにすぎません。
内部case
は長いオプションを手動で実装するため、いくつかのハウスキーピングが必要です。
bravo=?
は--bravo=foo
と一致しますが、--bravo=
とは一致しません(注:case
は最初の一致後に停止します)bravo*
が続き、--bravo
および--bravo=
に必要な引数が欠落していることに注意してくださいalpha* | charlie*
は、それらをサポートしないオプションに与えられた引数をキャッチします''
は、ダッシュで始まる非オプションをサポートするために存在します*
は、他のすべての長いオプションをキャッチし、無効なオプションに対してgetoptsによってスローされたエラーを再作成しますこれらのハウスキーピングアイテムのすべてが必ずしも必要なわけではありません。たとえば、--bravo
にoptional引数(-b
はgetopts
の制限のためサポートできない)が必要な場合があります。 =?
および関連するエラーケースを削除してから、${ARG_B:=$DEFAULT_ARG_B}
を初めて使用するときに$ARG_B
を呼び出します。
スペースで区切られた引数を持つ長いオプションを受け入れるには、(十分に安全な)eval
が必要です。
bravo=?* ) ARG_B="$LONG_OPTARG" ;;
bravo ) eval "ARG_B=\"\$$OPTIND\""
if [ -z "$ARG_B" ]; then
echo "No arg for --$OPTARG option" >&2; exit 2
fi
OPTIND=$((OPTIND+1)) ;;
bravo* ) echo "No arg for --$OPTARG option" >&2; exit 2 ;;
=
割り当てバージョンの直後に追加されたこの追加の句は、eval
を実行して、その時点で解釈されているオプションに続くパラメーターの値を解釈します。 Bashは 間接展開 、ARG_B="${!OPTIND}"
を使用してこれをより明確に実行でき、Zshは パラメーター展開P
フラグ 、ARGB="${(P)OPTIND}"
を使用してこれを実行できます、しかしeval
は完全に移植可能なPOSIXである必要があります。 $OPTIND
は、$2
(つまり、$1
は--bravo
を意味します)と言う、次のシェル引数を参照する番号です。 eval
はARG_B="$2"
を解釈します。これは引用符で囲まれているため、不正使用から安全です(不適切な操作を行う方法を見つけることができませんでした)。
空でない値を保証することは簡単ではなく、実際にチェックする必要があるため、その場合は条件付きで致命的なエラーを生成します。空にすることを許可する場合は、$OPTIND
の増分を条件付けするか、無限ループに陥る必要があります。 [ $# -gt $OPTIND ] && OPTIND=$((OPTIND+1))
する必要があります。
この追加句の最後の部分は、$OPTIND
をインクリメントして、オプションの引数を適切に吸収し、次のオプションに進みます。
shFlagsを見てください。 ksh、Linux上のzsh、Solarisなど。
それはあなたのスクリプトに1行を追加するのと同じくらい簡単に新しいフラグを追加することを可能にし、それは自動生成された使用法関数を提供します。
これは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}!"
長いオプションをサポートする強化されたgetoptを持っているOS(例えばLinux)のために、あなたはすることができます:
$ ./hello_world.sh --name Kate
Hello, Kate!
それ以外の場合は、短いオプションを使用する必要があります。
$ ./hello_world.sh -n Kate
Hello, Kate!
新しいフラグを追加するのは、新しいDEFINE_ call
を追加するのと同じくらい簡単です。
getopts
の使用Options=$@
Optnum=$#
sfoo='no '
sbar='no '
sfoobar='no '
sbarfoo='no '
sarguments='no '
sARG=empty
lfoo='no '
lbar='no '
lfoobar='no '
lbarfoo='no '
larguments='no '
lARG=empty
function _usage()
{
###### U S A G E : Help and ERROR ######
cat <<EOF
foobar $Options
$*
Usage: foobar <[options]>
Options:
-b --bar Set bar to yes ($foo)
-f --foo Set foo to yes ($bart)
-h --help Show this message
-A --arguments=... Set arguments to yes ($arguments) AND get ARGUMENT ($ARG)
-B --barfoo Set barfoo to yes ($barfoo)
-F --foobar Set foobar to yes ($foobar)
EOF
}
[ $# = 0 ] && _usage " >>>>>>>> no options given "
getops
while getopts ':bfh-A:BF' OPTION ; do
case "$OPTION" in
b ) sbar=yes ;;
f ) sfoo=yes ;;
h ) _usage ;;
A ) sarguments=yes;sARG="$OPTARG" ;;
B ) sbarfoo=yes ;;
F ) sfoobar=yes ;;
- ) [ $OPTIND -ge 1 ] && optind=$(expr $OPTIND - 1 ) || optind=$OPTIND
eval OPTION="\$$optind"
OPTARG=$(echo $OPTION | cut -d'=' -f2)
OPTION=$(echo $OPTION | cut -d'=' -f1)
case $OPTION in
--foo ) lfoo=yes ;;
--bar ) lbar=yes ;;
--foobar ) lfoobar=yes ;;
--barfoo ) lbarfoo=yes ;;
--help ) _usage ;;
--arguments ) larguments=yes;lARG="$OPTARG" ;;
* ) _usage " Long: >>>>>>>> invalid options (long) " ;;
esac
OPTIND=1
shift
;;
? ) _usage "Short: >>>>>>>> invalid options (short) " ;;
esac
done
##################################################################
echo "----------------------------------------------------------"
echo "RESULT short-foo : $sfoo long-foo : $lfoo"
echo "RESULT short-bar : $sbar long-bar : $lbar"
echo "RESULT short-foobar : $sfoobar long-foobar : $lfoobar"
echo "RESULT short-barfoo : $sbarfoo long-barfoo : $lbarfoo"
echo "RESULT short-arguments: $sarguments with Argument = \"$sARG\" long-arguments: $larguments and $lARG"
#!/bin/bash
# foobar: getopts with short and long options AND arguments
function _cleanup ()
{
unset -f _usage _cleanup ; return 0
}
## Clear out nested functions on exit
trap _cleanup INT EXIT RETURN
###### some declarations for this example ######
Options=$@
Optnum=$#
sfoo='no '
sbar='no '
sfoobar='no '
sbarfoo='no '
sarguments='no '
sARG=empty
lfoo='no '
lbar='no '
lfoobar='no '
lbarfoo='no '
larguments='no '
lARG=empty
function _usage()
{
###### U S A G E : Help and ERROR ######
cat <<EOF
foobar $Options
$*
Usage: foobar <[options]>
Options:
-b --bar Set bar to yes ($foo)
-f --foo Set foo to yes ($bart)
-h --help Show this message
-A --arguments=... Set arguments to yes ($arguments) AND get ARGUMENT ($ARG)
-B --barfoo Set barfoo to yes ($barfoo)
-F --foobar Set foobar to yes ($foobar)
EOF
}
[ $# = 0 ] && _usage " >>>>>>>> no options given "
##################################################################
####### "getopts" with: short options AND long options #######
####### AND short/long arguments #######
while getopts ':bfh-A:BF' OPTION ; do
case "$OPTION" in
b ) sbar=yes ;;
f ) sfoo=yes ;;
h ) _usage ;;
A ) sarguments=yes;sARG="$OPTARG" ;;
B ) sbarfoo=yes ;;
F ) sfoobar=yes ;;
- ) [ $OPTIND -ge 1 ] && optind=$(expr $OPTIND - 1 ) || optind=$OPTIND
eval OPTION="\$$optind"
OPTARG=$(echo $OPTION | cut -d'=' -f2)
OPTION=$(echo $OPTION | cut -d'=' -f1)
case $OPTION in
--foo ) lfoo=yes ;;
--bar ) lbar=yes ;;
--foobar ) lfoobar=yes ;;
--barfoo ) lbarfoo=yes ;;
--help ) _usage ;;
--arguments ) larguments=yes;lARG="$OPTARG" ;;
* ) _usage " Long: >>>>>>>> invalid options (long) " ;;
esac
OPTIND=1
shift
;;
? ) _usage "Short: >>>>>>>> invalid options (short) " ;;
esac
done
別の方法...
# translate long options to short
for arg
do
delim=""
case "$arg" in
--help) args="${args}-h ";;
--verbose) args="${args}-v ";;
--config) args="${args}-c ";;
# pass through anything else
*) [[ "${arg:0:1}" == "-" ]] || delim="\""
args="${args}${delim}${arg}${delim} ";;
esac
done
# reset the translated args
eval set -- $args
# now we can process with getopt
while getopts ":hvc:" opt; do
case $opt in
h) usage ;;
v) VERBOSE=true ;;
c) source $OPTARG ;;
\?) usage ;;
:)
echo "option -$OPTARG requires an argument"
usage
;;
esac
done
私はこのように解決しました:
# A string with command options
options=$@
# An array with all the arguments
arguments=($options)
# Loop index
index=0
for argument in $options
do
# Incrementing index
index=`expr $index + 1`
# The conditions
case $argument in
-a) echo "key $argument value ${arguments[index]}" ;;
-abc) echo "key $argument value ${arguments[index]}" ;;
esac
done
exit;
私はばかげているのか何かですか? getopt
とgetopts
はとても混乱します。
getopt
依存関係が必要ない場合は、これを実行できます。
while test $# -gt 0
do
case $1 in
# Normal option processing
-h | --help)
# usage and help
;;
-v | --version)
# version info
;;
# ...
# Special cases
--)
break
;;
--*)
# error unknown (long) option $1
;;
-?)
# error unknown (short) option $1
;;
# FUN STUFF HERE:
# Split apart combined short options
-*)
split=$1
shift
set -- $(echo "$split" | cut -c 2- | sed 's/./-& /g') "$@"
continue
;;
# Done with options
*)
break
;;
esac
# for testing purposes:
echo "$1"
shift
done
もちろん、それからあなたは1つのダッシュで長いスタイルのオプションを使うことはできません。また、短縮版(--verboseの代わりに--verbosなど)を追加する場合は、手動で追加する必要があります。
しかし、長いオプションと一緒にgetopts
機能を手に入れようと思っているなら、これはそれをする簡単な方法です。
このスニペットも 要旨 に入れます。
組み込みのgetopts
はこれができません。外部があります getopt(1)これを実行できるプログラムですが、Linuxからは util-linux パッケージ。サンプルスクリプトが付属しています getopt-parse.bash。
シェル関数として書かれた getopts_long
もあります。
#!/bin/bash
while getopts "abc:d:" flag
do
case $flag in
a) echo "[getopts:$OPTIND]==> -$flag";;
b) echo "[getopts:$OPTIND]==> -$flag";;
c) echo "[getopts:$OPTIND]==> -$flag $OPTARG";;
d) echo "[getopts:$OPTIND]==> -$flag $OPTARG";;
esac
done
shift $((OPTIND-1))
echo "[otheropts]==> $@"
exit
。
#!/bin/bash
until [ -z "$1" ]; do
case $1 in
"--dlong")
shift
if [ "${1:1:0}" != "-" ]
then
echo "==> dlong $1"
shift
fi;;
*) echo "==> other $1"; shift;;
esac
done
exit
ksh93
では、getopts
はロングネームをサポートします...
while getopts "f(file):s(server):" flag
do
echo "$flag" $OPTIND $OPTARG
done
それとも私が見つけたチュートリアルは言っています。試してみてください。
私は時々シェルスクリプトを書くだけで、慣れないので、どんなフィードバックでも感謝します。
@Arvid Requateによって提案された戦略を使用して、我々はいくつかのユーザーエラーに気づきました。値を含めるのを忘れたユーザーは、誤って次のオプションの名前を値として扱います。
./getopts_test.sh --loglevel= --toc=TRUE
"loglevel"の値は "--toc = TRUE"と見なされます。これは避けられます。
手動解析の http://mwiki.wooledge.org/BashFAQ/035 の議論から、CLIのユーザーエラーチェックに関するアイデアをいくつか修正しました。 " - "と " - "の両方の引数の処理にエラーチェックを挿入しました。
それから私は構文をいじって始めました、それでここでのどんな誤りも厳密に私のせいであり、原作者ではありません。
私のアプローチは、等号の有無にかかわらず長く入力することを好むユーザーに役立ちます。つまり、 " - loglevel 9"に対して "--loglevel = 9"と同じ応答があるはずです。 - /spaceメソッドでは、ユーザーが引数を忘れたかどうかを確実に知ることは不可能であるため、いくらかの推測が必要です。
あなたがこれを始めている場合には、 " - opt = value"と "--opt value"のフォーマットの間に興味深い違いがあります。等号では、コマンドライン引数は "opt = value"と見なされ、それを処理する作業は文字列解析で、 "="で区切ります。対照的に、 " - opt value"の場合、引数の名前は "opt"であり、次の値をコマンドラインで提供するという課題があります。そこで@Arvid Requateは間接参照である$ {!OPTIND}を使用しました。 BashFAQのコメントはそのスタイルに対して警告しているように思われます( http://mywiki.wooledge.org/BashFAQ/006 )。ところで、私は前回のポスターのOPTIND = $(($ OPTIND + 1))の重要性についてのコメントは正しいとは思いません。つまり、省略しても害はありません。
このスクリプトの最新版では、フラグ-vはVERBOSE印刷を意味します。
それを "cli-5.sh"と呼ばれるファイルに保存して実行可能にすると、これらのどれでもうまくいくか、あるいは望みの方法で失敗するでしょう。
./cli-5.sh -v --loglevel=44 --toc TRUE
./cli-5.sh -v --loglevel=44 --toc=TRUE
./cli-5.sh --loglevel 7
./cli-5.sh --loglevel=8
./cli-5.sh -l9
./cli-5.sh --toc FALSE --loglevel=77
./cli-5.sh --toc=FALSE --loglevel=77
./cli-5.sh -l99 -t yyy
./cli-5.sh -l 99 -t yyy
これはユーザintpuのエラーチェックの出力例です
$ ./cli-5.sh --toc --loglevel=77
ERROR: toc value must not have dash at beginning
$ ./cli-5.sh --toc= --loglevel=77
ERROR: value for toc undefined
OPTINDとOPTARGの内部を出力するため、-vをオンにすることを検討してください。
#/usr/bin/env bash
## Paul Johnson
## 20171016
##
## Combines ideas from
## https://stackoverflow.com/questions/402377/using-getopts-in-bash-Shell-script-to-get-long-and-short-command-line-options
## by @Arvid Requate, and http://mwiki.wooledge.org/BashFAQ/035
# What I don't understand yet:
# In @Arvid REquate's answer, we have
# val="${!OPTIND}"; OPTIND=$(( $OPTIND + 1 ))
# this works, but I don't understand it!
die() {
printf '%s\n' "$1" >&2
exit 1
}
printparse(){
if [ ${VERBOSE} -gt 0 ]; then
printf 'Parse: %s%s%s\n' "$1" "$2" "$3" >&2;
fi
}
showme(){
if [ ${VERBOSE} -gt 0 ]; then
printf 'VERBOSE: %s\n' "$1" >&2;
fi
}
VERBOSE=0
loglevel=0
toc="TRUE"
optspec=":vhl:t:-:"
while getopts "$optspec" OPTCHAR; do
showme "OPTARG: ${OPTARG[*]}"
showme "OPTIND: ${OPTIND[*]}"
case "${OPTCHAR}" in
-)
case "${OPTARG}" in
loglevel) #argument has no equal sign
opt=${OPTARG}
val="${!OPTIND}"
## check value. If negative, assume user forgot value
showme "OPTIND is {$OPTIND} {!OPTIND} has value \"${!OPTIND}\""
if [[ "$val" == -* ]]; then
die "ERROR: $opt value must not have dash at beginning"
fi
## OPTIND=$(( $OPTIND + 1 )) # CAUTION! no effect?
printparse "--${OPTARG}" " " "${val}"
loglevel="${val}"
shift
;;
loglevel=*) #argument has equal sign
opt=${OPTARG%=*}
val=${OPTARG#*=}
if [ "${OPTARG#*=}" ]; then
printparse "--${opt}" "=" "${val}"
loglevel="${val}"
## shift CAUTION don't shift this, fails othewise
else
die "ERROR: $opt value must be supplied"
fi
;;
toc) #argument has no equal sign
opt=${OPTARG}
val="${!OPTIND}"
## check value. If negative, assume user forgot value
showme "OPTIND is {$OPTIND} {!OPTIND} has value \"${!OPTIND}\""
if [[ "$val" == -* ]]; then
die "ERROR: $opt value must not have dash at beginning"
fi
## OPTIND=$(( $OPTIND + 1 )) #??
printparse "--${opt}" " " "${val}"
toc="${val}"
shift
;;
toc=*) #argument has equal sign
opt=${OPTARG%=*}
val=${OPTARG#*=}
if [ "${OPTARG#*=}" ]; then
toc=${val}
printparse "--$opt" " -> " "$toc"
##shift ## NO! dont shift this
else
die "ERROR: value for $opt undefined"
fi
;;
help)
echo "usage: $0 [-v] [--loglevel[=]<value>] [--toc[=]<TRUE,FALSE>]" >&2
exit 2
;;
*)
if [ "$OPTERR" = 1 ] && [ "${optspec:0:1}" != ":" ]; then
echo "Unknown option --${OPTARG}" >&2
fi
;;
esac;;
h|-\?|--help)
## must rewrite this for all of the arguments
echo "usage: $0 [-v] [--loglevel[=]<value>] [--toc[=]<TRUE,FALSE>]" >&2
exit 2
;;
l)
loglevel=${OPTARG}
printparse "-l" " " "${loglevel}"
;;
t)
toc=${OPTARG}
;;
v)
VERBOSE=1
;;
*)
if [ "$OPTERR" != 1 ] || [ "${optspec:0:1}" = ":" ]; then
echo "Non-option argument: '-${OPTARG}'" >&2
fi
;;
esac
done
echo "
After Parsing values
"
echo "loglevel $loglevel"
echo "toc $toc"
車輪のさらに別のバージョンを発明しています...
この関数はGNU getoptに代わる(うまくいけば)POSIX互換のプレーンボーンシェルです。必須/オプション/引数なしを受け入れることができるshort/longオプションをサポートしており、オプションの指定方法はGNU getoptとほぼ同じなので、変換は簡単です。
もちろん、これはスクリプトに入れるにはまだかなりのサイズのコードですが、よく知られているgetopt_longシェル関数の約半分の行であり、既存のGNUを置き換えたい場合にはこれが望ましいかもしれません。 _ getoptが使用します。
これはかなり新しいコードなので、YMMV(そして、これが実際にPOSIXと互換性がないという理由で間違いなく私に知らせてください - 移植性は当初からの意図でしたが、私は役に立つPOSIXテスト環境を持っていません).
コードと使用例は次のとおりです。
#!/bin/sh
# posix_getopt Shell function
# Author: Phil S.
# Version: 1.0
# Created: 2016-07-05
# URL: http://stackoverflow.com/a/37087374/324105
# POSIX-compatible argument quoting and parameter save/restore
# http://www.etalabs.net/sh_tricks.html
# Usage:
# parameters=$(save "$@") # save the original parameters.
# eval "set -- ${parameters}" # restore the saved parameters.
save () {
local param
for param; do
printf %s\\n "$param" \
| sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/"
done
printf %s\\n " "
}
# Exit with status $1 after displaying error message $2.
exiterr () {
printf %s\\n "$2" >&2
exit $1
}
# POSIX-compatible command line option parsing.
# This function supports long options and optional arguments, and is
# a (largely-compatible) drop-in replacement for GNU getopt.
#
# Instead of:
# opts=$(getopt -o "$shortopts" -l "$longopts" -- "$@")
# eval set -- ${opts}
#
# We instead use:
# opts=$(posix_getopt "$shortopts" "$longopts" "$@")
# eval "set -- ${opts}"
posix_getopt () { # args: "$shortopts" "$longopts" "$@"
local shortopts longopts \
arg argtype getopt nonopt opt optchar optword suffix
shortopts="$1"
longopts="$2"
shift 2
getopt=
nonopt=
while [ $# -gt 0 ]; do
opt=
arg=
argtype=
case "$1" in
# '--' means don't parse the remaining options
( -- ) {
getopt="${getopt}$(save "$@")"
shift $#
break
};;
# process short option
( -[!-]* ) { # -x[foo]
suffix=${1#-?} # foo
opt=${1%$suffix} # -x
optchar=${opt#-} # x
case "${shortopts}" in
( *${optchar}::* ) { # optional argument
argtype=optional
arg="${suffix}"
shift
};;
( *${optchar}:* ) { # required argument
argtype=required
if [ -n "${suffix}" ]; then
arg="${suffix}"
shift
else
case "$2" in
( -* ) exiterr 1 "$1 requires an argument";;
( ?* ) arg="$2"; shift 2;;
( * ) exiterr 1 "$1 requires an argument";;
esac
fi
};;
( *${optchar}* ) { # no argument
argtype=none
arg=
shift
# Handle multiple no-argument parameters combined as
# -xyz instead of -x -y -z. If we have just shifted
# parameter -xyz, we now replace it with -yz (which
# will be processed in the next iteration).
if [ -n "${suffix}" ]; then
eval "set -- $(save "-${suffix}")$(save "$@")"
fi
};;
( * ) exiterr 1 "Unknown option $1";;
esac
};;
# process long option
( --?* ) { # --xarg[=foo]
suffix=${1#*=} # foo (unless there was no =)
if [ "${suffix}" = "$1" ]; then
suffix=
fi
opt=${1%=$suffix} # --xarg
optword=${opt#--} # xarg
case ",${longopts}," in
( *,${optword}::,* ) { # optional argument
argtype=optional
arg="${suffix}"
shift
};;
( *,${optword}:,* ) { # required argument
argtype=required
if [ -n "${suffix}" ]; then
arg="${suffix}"
shift
else
case "$2" in
( -* ) exiterr 1 \
"--${optword} requires an argument";;
( ?* ) arg="$2"; shift 2;;
( * ) exiterr 1 \
"--${optword} requires an argument";;
esac
fi
};;
( *,${optword},* ) { # no argument
if [ -n "${suffix}" ]; then
exiterr 1 "--${optword} does not take an argument"
fi
argtype=none
arg=
shift
};;
( * ) exiterr 1 "Unknown option $1";;
esac
};;
# any other parameters starting with -
( -* ) exiterr 1 "Unknown option $1";;
# remember non-option parameters
( * ) nonopt="${nonopt}$(save "$1")"; shift;;
esac
if [ -n "${opt}" ]; then
getopt="${getopt}$(save "$opt")"
case "${argtype}" in
( optional|required ) {
getopt="${getopt}$(save "$arg")"
};;
esac
fi
done
# Generate function output, suitable for:
# eval "set -- $(posix_getopt ...)"
printf %s "${getopt}"
if [ -n "${nonopt}" ]; then
printf %s "$(save "--")${nonopt}"
fi
}
使用例
# Process command line options
shortopts="hvd:c::s::L:D"
longopts="help,version,directory:,client::,server::,load:,delete"
#opts=$(getopt -o "$shortopts" -l "$longopts" -n "$(basename $0)" -- "$@")
opts=$(posix_getopt "$shortopts" "$longopts" "$@")
if [ $? -eq 0 ]; then
#eval set -- ${opts}
eval "set -- ${opts}"
while [ $# -gt 0 ]; do
case "$1" in
( -- ) shift; break;;
( -h|--help ) help=1; shift; break;;
( -v|--version ) version_help=1; shift; break;;
( -d|--directory ) dir=$2; shift 2;;
( -c|--client ) useclient=1; client=$2; shift 2;;
( -s|--server ) startserver=1; server_name=$2; shift 2;;
( -L|--load ) load=$2; shift 2;;
( -D|--delete ) delete=1; shift;;
esac
done
else
shorthelp=1 # getopt returned (and reported) an error.
fi
ここでは、bashで複雑なオプションを解析するためのいくつかの異なるアプローチを見つけることができます。 http://mywiki.wooledge.org/ComplexOptionParsing
私は次のコードを作成しましたが、最小限のコードで、長いオプションと短いオプションの両方が機能するので、これは良いものだと思います。このアプローチでは、長いオプションにも複数の引数があります。
#!/bin/bash
# Uses bash extensions. Not portable as written.
declare -A longoptspec
longoptspec=( [loglevel]=1 ) #use associative array to declare how many arguments a long option expects, in this case we declare that loglevel expects/has one argument, long options that aren't listed i n this way will have zero arguments by default
optspec=":h-:"
while getopts "$optspec" opt; do
while true; do
case "${opt}" in
-) #OPTARG is name-of-long-option or name-of-long-option=value
if [[ "${OPTARG}" =~ .*=.* ]] #with this --key=value format only one argument is possible
then
opt=${OPTARG/=*/}
OPTARG=${OPTARG#*=}
((OPTIND--))
else #with this --key value1 value2 format multiple arguments are possible
opt="$OPTARG"
OPTARG=(${@:OPTIND:$((longoptspec[$opt]))})
fi
((OPTIND+=longoptspec[$opt]))
continue #now that opt/OPTARG are set we can process them as if getopts would've given us long options
;;
loglevel)
loglevel=$OPTARG
;;
h|help)
echo "usage: $0 [--loglevel[=]<value>]" >&2
exit 2
;;
esac
break; done
done
# End of file
改善された解決策:
# translate long options to short
# Note: This enable long options but disable "--?*" in $OPTARG, or disable long options after "--" in option fields.
for ((i=1;$#;i++)) ; do
case "$1" in
--)
# [ ${args[$((i-1))]} == ... ] || EndOpt=1 ;;& # DIRTY: we still can handle some execptions...
EndOpt=1 ;;&
--version) ((EndOpt)) && args[$i]="$1" || args[$i]="-V";;
# default case : short option use the first char of the long option:
--?*) ((EndOpt)) && args[$i]="$1" || args[$i]="-${1:2:1}";;
# pass through anything else:
*) args[$i]="$1" ;;
esac
shift
done
# reset the translated args
set -- "${args[@]}"
function usage {
echo "Usage: $0 [options] files" >&2
exit $1
}
# now we can process with getopt
while getopts ":hvVc:" opt; do
case $opt in
h) usage ;;
v) VERBOSE=true ;;
V) echo $Version ; exit ;;
c) source $OPTARG ;;
\?) echo "unrecognized option: -$opt" ; usage -1 ;;
:)
echo "option -$OPTARG requires an argument"
usage -1
;;
esac
done
shift $((OPTIND-1))
[[ "$1" == "--" ]] && shift
長いコマンドラインオプションが必要な場合は、getopts部分だけにkshを使用する方が簡単かもしれません。
# Working Getopts Long => KSH
#! /bin/ksh
# Getopts Long
USAGE="s(showconfig)"
USAGE+="c:(createdb)"
USAGE+="l:(createlistener)"
USAGE+="g:(generatescripts)"
USAGE+="r:(removedb)"
USAGE+="x:(removelistener)"
USAGE+="t:(createtemplate)"
USAGE+="h(help)"
while getopts "$USAGE" optchar ; do
case $optchar in
s) echo "Displaying Configuration" ;;
c) echo "Creating Database $OPTARG" ;;
l) echo "Creating Listener LISTENER_$OPTARG" ;;
g) echo "Generating Scripts for Database $OPTARG" ;;
r) echo "Removing Database $OPTARG" ;;
x) echo "Removing Listener LISTENER_$OPTARG" ;;
t) echo "Creating Database Template" ;;
h) echo "Help" ;;
esac
done
厳密なbashサポート(-u)を備えた、外部の依存関係のないものが欲しくて、私はそれが古いbashバージョンでさえ動作するために必要でした。これはさまざまなタイプのパラメータを処理します。
スクリプトの先頭に以下を挿入するだけです。
# Check if a list of params contains a specific param
# usage: if _param_variant "h|?|help p|path f|file long-thing t|test-thing" "file" ; then ...
# the global variable $key is updated to the long notation (last entry in the pipe delineated list, if applicable)
_param_variant() {
for param in $1 ; do
local variants=${param//\|/ }
for variant in $variants ; do
if [[ "$variant" = "$2" ]] ; then
# Update the key to match the long version
local arr=(${param//\|/ })
let last=${#arr[@]}-1
key="${arr[$last]}"
return 0
fi
done
done
return 1
}
# Get input parameters in short or long notation, with no dependencies beyond bash
# usage:
# # First, set your defaults
# param_help=false
# param_path="."
# param_file=false
# param_image=false
# param_image_lossy=true
# # Define allowed parameters
# allowed_params="h|?|help p|path f|file i|image image-lossy"
# # Get parameters from the arguments provided
# _get_params $*
#
# Parameters will be converted into safe variable names like:
# param_help,
# param_path,
# param_file,
# param_image,
# param_image_lossy
#
# Parameters without a value like "-h" or "--help" will be treated as
# boolean, and will be set as param_help=true
#
# Parameters can accept values in the various typical ways:
# -i "path/goes/here"
# --image "path/goes/here"
# --image="path/goes/here"
# --image=path/goes/here
# These would all result in effectively the same thing:
# param_image="path/goes/here"
#
# Concatinated short parameters (boolean) are also supported
# -vhm is the same as -v -h -m
_get_params(){
local param_pair
local key
local value
local shift_count
while : ; do
# Ensure we have a valid param. Allows this to work even in -u mode.
if [[ $# == 0 || -z $1 ]] ; then
break
fi
# Split the argument if it contains "="
param_pair=(${1//=/ })
# Remove preceeding dashes
key="${param_pair[0]#--}"
# Check for concatinated boolean short parameters.
local nodash="${key#-}"
local breakout=false
if [[ "$nodash" != "$key" && ${#nodash} -gt 1 ]]; then
# Extrapolate multiple boolean keys in single dash notation. ie. "-vmh" should translate to: "-v -m -h"
local short_param_count=${#nodash}
let new_arg_count=$#+$short_param_count-1
local new_args=""
# $str_pos is the current position in the short param string $nodash
for (( str_pos=0; str_pos<new_arg_count; str_pos++ )); do
# The first character becomes the current key
if [ $str_pos -eq 0 ] ; then
key="${nodash:$str_pos:1}"
breakout=true
fi
# $arg_pos is the current position in the constructed arguments list
let arg_pos=$str_pos+1
if [ $arg_pos -gt $short_param_count ] ; then
# handle other arguments
let orignal_arg_number=$arg_pos-$short_param_count+1
local new_arg="${!orignal_arg_number}"
else
# break out our one argument into new ones
local new_arg="-${nodash:$str_pos:1}"
fi
new_args="$new_args \"$new_arg\""
done
# remove the preceding space and set the new arguments
eval set -- "${new_args# }"
fi
if ! $breakout ; then
key="$nodash"
fi
# By default we expect to shift one argument at a time
shift_count=1
if [ "${#param_pair[@]}" -gt "1" ] ; then
# This is a param with equals notation
value="${param_pair[1]}"
else
# This is either a boolean param and there is no value,
# or the value is the next command line argument
# Assume the value is a boolean true, unless the next argument is found to be a value.
value=true
if [[ $# -gt 1 && -n "$2" ]]; then
local nodash="${2#-}"
if [ "$nodash" = "$2" ]; then
# The next argument has NO preceding dash so it is a value
value="$2"
shift_count=2
fi
fi
fi
# Check that the param being passed is one of the allowed params
if _param_variant "$allowed_params" "$key" ; then
# --key-name will now become param_key_name
eval param_${key//-/_}="$value"
else
printf 'WARNING: Unknown option (ignored): %s\n' "$1" >&2
fi
shift $shift_count
done
}
そしてそれを次のように使います。
# Assign defaults for parameters
param_help=false
param_path=$(pwd)
param_file=false
param_image=true
param_image_lossy=true
param_image_lossy_quality=85
# Define the params we will allow
allowed_params="h|?|help p|path f|file i|image image-lossy image-lossy-quality"
# Get the params from arguments provided
_get_params $*
私は長い間そのテーマに取り組んできました...そしてあなたがあなたのメインスクリプトでソースにする必要がある私自身のライブラリを作りました。例として、 libopt4Shell および cd2mpc を参照してください。それが役に立てば幸い !
私は彼の解決策をコメントしたり投票したりするのに十分な担当者を持っていませんが、 smeの答え は私にとって非常にうまくいきました。私が遭遇した唯一の問題は、引数が一重引用符で囲まれてしまうということでした(だから私はそれらを取り除きました)。
私はまたいくつかの使用例とヘルプテキストを追加しました。ここに私のわずかに拡張されたバージョンを含めます。
#!/bin/bash
# getopt example
# from: https://stackoverflow.com/questions/402377/using-getopts-in-bash-Shell-script-to-get-long-and-short-command-line-options
HELP_TEXT=\
" USAGE:\n
Accepts - and -- flags, can specify options that require a value, and can be in any order. A double-hyphen (--) will stop processing options.\n\n
Accepts the following forms:\n\n
getopt-example.sh -a -b -c value-for-c some-arg\n
getopt-example.sh -c value-for-c -a -b some-arg\n
getopt-example.sh -abc some-arg\n
getopt-example.sh --along --blong --clong value-for-c -a -b -c some-arg\n
getopt-example.sh some-arg --clong value-for-c\n
getopt-example.sh
"
aflag=false
bflag=false
cargument=""
# options may be followed by one colon to indicate they have a required argument
if ! options=$(getopt -o abc:h\? -l along,blong,help,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=true ;;
-b|--blong) bflag=true ;;
# for options with required arguments, an additional shift is required
-c|--clong) cargument="$2" ; shift;;
-h|--help|-\?) echo -e $HELP_TEXT; exit;;
(--) shift; break;;
(-*) echo "$0: error - unrecognized option $1" 1>&2; exit 1;;
(*) break;;
esac
shift
done
# to remove the single quotes around arguments, pipe the output into:
# | sed -e "s/^'\\|'$//g" (just leading/trailing) or | sed -e "s/'//g" (all)
echo aflag=${aflag}
echo bflag=${bflag}
echo cargument=${cargument}
while [ $# -gt 0 ]
do
echo arg=$1
shift
if [[ $aflag == true ]]; then
echo a is true
fi
done
受け入れられた答えは、bashに組み込まれたgetopts
のすべての欠点を指摘するという非常にいい仕事をします。答えは以下で終わります。
それで、長いオプションのサポートの欠如を回避するためにもっと多くのコードを書くことは可能ですが、これはもっと多くの仕事であり、コードを単純化するためにgetoptパーサーを使う目的を部分的に無効にします。
私は原則としてその声明に同意しますが、私たち全員がさまざまなスクリプトでこの機能を実装した回数は、「標準化された」十分にテストされたソリューションを作成することに少しの努力を払うことを正当化すると思います。
そのため、私は getopts_long
を純粋なbashで実装することによってgetopts
に組み込まれたbashを「アップグレード」しました。外部の依存関係はありません。この関数の使い方は組み込みのgetopts
と100%互換です。
getopts_long
(GitHubの https://github.com/UmkaDK/getopts_long でホストされています)をスクリプトに含めることで、元の質問に対する回答が得られます。以下のように単純に実装することができます。
source "${PATH_TO}/getopts_long.bash"
while getopts_long ':c: copyfile:' OPTKEY; do
case ${OPTKEY} in
'c'|'copyfile')
echo 'file supplied -- ${OPTARG}'
;;
'?')
echo "INVALID OPTION -- ${OPTARG}" >&2
exit 1
;;
':')
echo "MISSING ARGUMENT for option -- ${OPTARG}" >&2
exit 1
;;
*)
echo "Misconfigured OPTSPEC or uncaught option -- ${OPTKEY}" >&2
exit 1
;;
esac
done
shift $(( OPTIND - 1 ))
[[ "${1}" == "--" ]] && shift
内蔵のOS X(BSD)getoptは長いオプションをサポートしていませんが、GNUバージョンはbrew install gnu-getopt
をサポートしています。それから、cp /usr/local/Cellar/gnu-getopt/1.1.6/bin/getopt /usr/local/bin/gnu-getopt
のようなものになります。
長い名前の引数だけを取得するための簡単な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 "$@"
プラットフォーム間の互換性を保ち、外部の実行可能ファイルへの依存を避けるために、私はいくつかのコードを別の言語から移植しました。
私はそれが非常に使いやすいと思います、ここに例があります:
ArgParser::addArg "[h]elp" false "This list"
ArgParser::addArg "[q]uiet" false "Supress output"
ArgParser::addArg "[s]leep" 1 "Seconds to sleep"
ArgParser::addArg "v" 1 "Verbose mode"
ArgParser::parse "$@"
ArgParser::isset help && ArgParser::showArgs
ArgParser::isset "quiet" \
&& echo "Quiet!" \
|| echo "Noisy!"
local __sleep
ArgParser::tryAndGetArg sleep into __sleep \
&& echo "Sleep for $__sleep seconds" \
|| echo "No value passed for sleep"
# This way is often more convienient, but is a little slower
echo "Sleep set to: $( ArgParser::getArg sleep )"
必要なBASHはそれよりも少し長いですが、私はBASH 4の連想配列への依存を避けたいと思いました。こちらから直接ダウンロードすることもできます http://nt4.com/bash/argparser.inc.sh
#!/usr/bin/env bash
# Updates to this script may be found at
# http://nt4.com/bash/argparser.inc.sh
# Example of runtime usage:
# mnc.sh --nc -q Caprica.S0*mkv *.avi *.mp3 --more-options here --Host centos8.Host.com
# Example of use in script (see bottom)
# Just include this file in yours, or use
# source argparser.inc.sh
unset EXPLODED
declare -a EXPLODED
function explode
{
local c=$#
(( c < 2 )) &&
{
echo function "$0" is missing parameters
return 1
}
local delimiter="$1"
local string="$2"
local limit=${3-99}
local tmp_delim=$'\x07'
local delin=${string//$delimiter/$tmp_delim}
local oldifs="$IFS"
IFS="$tmp_delim"
EXPLODED=($delin)
IFS="$oldifs"
}
# See: http://fvue.nl/wiki/Bash:_Passing_variables_by_reference
# Usage: local "$1" && upvar $1 "value(s)"
upvar() {
if unset -v "$1"; then # Unset & validate varname
if (( $# == 2 )); then
eval $1=\"\$2\" # Return single value
else
eval $1=\(\"\${@:2}\"\) # Return array
fi
fi
}
function decho
{
:
}
function ArgParser::check
{
__args=${#__argparser__arglist[@]}
for (( i=0; i<__args; i++ ))
do
matched=0
explode "|" "${__argparser__arglist[$i]}"
if [ "${#1}" -eq 1 ]
then
if [ "${1}" == "${EXPLODED[0]}" ]
then
decho "Matched $1 with ${EXPLODED[0]}"
matched=1
break
fi
else
if [ "${1}" == "${EXPLODED[1]}" ]
then
decho "Matched $1 with ${EXPLODED[1]}"
matched=1
break
fi
fi
done
(( matched == 0 )) && return 2
# decho "Key $key has default argument of ${EXPLODED[3]}"
if [ "${EXPLODED[3]}" == "false" ]
then
return 0
else
return 1
fi
}
function ArgParser::set
{
key=$3
value="${1:-true}"
declare -g __argpassed__$key="$value"
}
function ArgParser::parse
{
unset __argparser__argv
__argparser__argv=()
# echo parsing: "$@"
while [ -n "$1" ]
do
# echo "Processing $1"
if [ "${1:0:2}" == '--' ]
then
key=${1:2}
value=$2
Elif [ "${1:0:1}" == '-' ]
then
key=${1:1} # Strip off leading -
value=$2
else
decho "Not argument or option: '$1'" >& 2
__argparser__argv+=( "$1" )
shift
continue
fi
# parameter=${tmp%%=*} # Extract name.
# value=${tmp##*=} # Extract value.
decho "Key: '$key', value: '$value'"
# eval $parameter=$value
ArgParser::check $key
el=$?
# echo "Check returned $el for $key"
[ $el -eq 2 ] && decho "No match for option '$1'" >&2 # && __argparser__argv+=( "$1" )
[ $el -eq 0 ] && decho "Matched option '${EXPLODED[2]}' with no arguments" >&2 && ArgParser::set true "${EXPLODED[@]}"
[ $el -eq 1 ] && decho "Matched option '${EXPLODED[2]}' with an argument of '$2'" >&2 && ArgParser::set "$2" "${EXPLODED[@]}" && shift
shift
done
}
function ArgParser::isset
{
declare -p "__argpassed__$1" > /dev/null 2>&1 && return 0
return 1
}
function ArgParser::getArg
{
# This one would be a bit silly, since we can only return non-integer arguments ineffeciently
varname="__argpassed__$1"
echo "${!varname}"
}
##
# usage: tryAndGetArg <argname> into <varname>
# returns: 0 on success, 1 on failure
function ArgParser::tryAndGetArg
{
local __varname="__argpassed__$1"
local __value="${!__varname}"
test -z "$__value" && return 1
local "$3" && upvar $3 "$__value"
return 0
}
function ArgParser::__construct
{
unset __argparser__arglist
# declare -a __argparser__arglist
}
##
# @brief add command line argument
# @param 1 short and/or long, eg: [s]hort
# @param 2 default value
# @param 3 description
##
function ArgParser::addArg
{
# check for short arg within long arg
if [[ "$1" =~ \[(.)\] ]]
then
short=${BASH_REMATCH[1]}
long=${1/\[$short\]/$short}
else
long=$1
fi
if [ "${#long}" -eq 1 ]
then
short=$long
long=''
fi
decho short: "$short"
decho long: "$long"
__argparser__arglist+=("$short|$long|$1|$2|$3")
}
##
# @brief show available command line arguments
##
function ArgParser::showArgs
{
# declare -p | grep argparser
printf "Usage: %s [OPTION...]\n\n" "$( basename "${BASH_SOURCE[0]}" )"
printf "Defaults for the options are specified in brackets.\n\n";
__args=${#__argparser__arglist[@]}
for (( i=0; i<__args; i++ ))
do
local shortname=
local fullname=
local default=
local description=
local comma=
explode "|" "${__argparser__arglist[$i]}"
shortname="${EXPLODED[0]:+-${EXPLODED[0]}}" # String Substitution Guide:
fullname="${EXPLODED[1]:+--${EXPLODED[1]}}" # http://tldp.org/LDP/abs/html/parameter-substitution.html
test -n "$shortname" \
&& test -n "$fullname" \
&& comma=","
default="${EXPLODED[3]}"
case $default in
false )
default=
;;
"" )
default=
;;
* )
default="[$default]"
esac
description="${EXPLODED[4]}"
printf " %2s%1s %-19s %s %s\n" "$shortname" "$comma" "$fullname" "$description" "$default"
done
}
function ArgParser::test
{
# Arguments with a default of 'false' do not take paramaters (note: default
# values are not applied in this release)
ArgParser::addArg "[h]elp" false "This list"
ArgParser::addArg "[q]uiet" false "Supress output"
ArgParser::addArg "[s]leep" 1 "Seconds to sleep"
ArgParser::addArg "v" 1 "Verbose mode"
ArgParser::parse "$@"
ArgParser::isset help && ArgParser::showArgs
ArgParser::isset "quiet" \
&& echo "Quiet!" \
|| echo "Noisy!"
local __sleep
ArgParser::tryAndGetArg sleep into __sleep \
&& echo "Sleep for $__sleep seconds" \
|| echo "No value passed for sleep"
# This way is often more convienient, but is a little slower
echo "Sleep set to: $( ArgParser::getArg sleep )"
echo "Remaining command line: ${__argparser__argv[@]}"
}
if [ "$( basename "$0" )" == "argparser.inc.sh" ]
then
ArgParser::test "$@"
fi
EasyOptions は、短いオプションと長いオプションを処理します。
## Options:
## --verbose, -v Verbose mode
## --logfile=NAME Log filename
source easyoptions || exit
if test -n "${verbose}"; then
echo "log file: ${logfile}"
echo "arguments: ${arguments[@]}"
fi
組み込みのgetopts
は短いオプションのみを解析します(ksh93を除く)が、getoptsに長いオプションを処理させるために数行のスクリプトを追加することができます。
これは http://www.uxora.com/unix/Shell-script/22-handle-long-options-with-getopts にあるコードの一部です。
#== set short options ==#
SCRIPT_OPTS=':fbF:B:-:h'
#== set long options associated with short one ==#
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 を参照)
すべてのあなたの長い選択肢が短い選択肢としてユニークで一致する最初の文字を持っていれば、例えば
./slamm --chaos 23 --plenty test -quiet
と同じです
./slamm -c 23 -p test -q
$ argsを書き直すには、をgetoptsの前に使用できます。
# change long options to short options
for arg; do
[[ "${arg:0:1}" == "-" ]] && delim="" || delim="\""
if [ "${arg:0:2}" == "--" ];
then args="${args} -${arg:2:1}"
else args="${args} ${delim}${arg}${delim}"
fi
done
# reset the incoming args
eval set -- $args
# proceed as usual
while getopts ":b:la:h" OPTION; do
.....
Mtveeにインスピレーションを与えてくれてありがとう;-)
単にこれがあなたがスクリプトを呼び出す方法であるならば
myscript.sh --input1 "ABC" --input2 "PQR" --input2 "XYZ"
それなら、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