I wish to have long and short forms of command line options invoked using my shell script.
I know that getopts
can be used, but like in Perl, I have not been able to do the same with shell.
Any ideas on how this can be done, so that I can use options like:
./shell.sh --copyfile abc.pl /tmp/
./shell.sh -c abc.pl /tmp/
In the above, both the commands mean the same thing to my shell, but using getopts
, I have not been able to implement these?
ベストアンサー1
getopt
and getopts
are different beasts, and people seem to have a bit of misunderstanding of what they do. getopts
is a built-in command to bash
to process command-line options in a loop and assign each found option and value in turn to built-in variables, so you can further process them. getopt
, however, is an external utility program, and it doesn't actually process your options for you the way that e.g. bash getopts
, the Perl Getopt
module or the Python optparse
/argparse
modules do. All that getopt
does is canonicalize the options that are passed in — i.e. convert them to a more standard form, so that it's easier for a shell script to process them. For example, an application of getopt
might convert the following:
myscript -ab infile.txt -ooutfile.txt
into this:
myscript -a -b -o outfile.txt infile.txt
You have to do the actual processing yourself. You don't have to use getopt
at all if you make various restrictions on the way you can specify options:
- only put one option per argument;
- all options go before any positional parameters (i.e. non-option arguments);
- for options with values (e.g.
-o
above), the value has to go as a separate argument (after a space).
Why use getopt
instead of getopts
? The basic reason is that only GNU getopt
gives you support for long-named command-line options.1 (GNU getopt
is the default on Linux. Mac OS X and FreeBSD come with a basic and not-very-useful getopt
, but the GNU version can be installed; see below.)
For example, here's an example of using GNU getopt
, from a script of mine called 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
This lets you specify options like --verbose -dm4096 --minh=20 --maxhe 40 --debugfi="/Users/John Johnson/debug.txt"
or similar. The effect of the call to getopt
is to canonicalize the options to --verbose -d -m 4096 --minheap 20 --maxheap 40 --debugfile "/Users/John Johnson/debug.txt"
so that you can more easily process them. The quoting around "$1"
and "$2"
is important as it ensures that arguments with spaces in them get handled properly.
最初の 9 行 ( 行までのすべてeval set
) を削除しても、コードはのまま動作します。ただし、コードが受け入れるオプションの種類は、はるかに厳しくなります。特に、すべてのオプションを上記の「標準」形式で指定する必要があります。ただし、 を使用するとgetopt
、1 文字のオプションをグループ化したり、長いオプションのより短くてあいまいでない形式を使用したり、 または--file foo.txt
スタイル--file=foo.txt
のいずれかを使用したり、 または スタイルのいずれかを-m 4096
使用したり-m4096
、オプションと非オプションを任意の順序で組み合わせたりすることができます。getopt
また、認識されないオプションやあいまいなオプションが見つかった場合は、 からエラー メッセージが出力されます。
注: 実際には、 にはbasicと GNUの 2 つのまったく異なるバージョンがあり、機能も呼び出し規約も異なります。2 Basic はまったく壊れています。長いオプションを処理できないだけでなく、引数内の埋め込みスペースや空の引数も処理できませんが、 はこれを正しく処理します。上記のコードは basic では動作しません。GNU はLinux ではデフォルトでインストールされていますが、Mac OS X と FreeBSD では別途インストールする必要があります。Mac OS X では、MacPorts (getopt
getopt
getopt
getopt
getopts
getopt
getopt
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
。値を取るオプションの後にはコロンを付けます。
set
最後に、の代わりにだけがあるコードを見つけた場合eval set
、それは BSD 用に書かれていますgetopt
。 スタイルを使用するように変更する必要があります。eval set
このスタイルは の両方のバージョンで正常に動作しますgetopt
が、プレーンな はset
GNU では正しく動作しませんgetopt
。
1実際、getopts
ではksh93
長い名前のオプションがサポートされていますが、このシェルは ほど頻繁には使用されませんbash
。 ではzsh
、zparseopts
この機能を利用するために を使用します。
2技術的には、「GNU getopt
」は誤った名称です。このバージョンは実際には GNU プロジェクトではなく Linux 用に作成されました。ただし、すべての GNU 規則に従っており、「GNU」という用語はgetopt
一般的に使用されています (FreeBSD など)。