Using getopts to process long and short command line options Ask Question

Using getopts to process long and short command line options Ask Question

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 (getoptgetoptgetoptgetoptgetoptsgetoptgetopthttp://www.macports.org) を実行してから、 を実行してsudo port install getoptGNU をインストールしgetopt(通常は に/opt/local/bin)、 が/opt/local/binより前にシェル パスにあることを確認します/usr/bin。FreeBSD では、 をインストールしますmisc/getopt

独自のプログラム用にサンプル コードを変更するための簡単なガイド: 最初の数行は、 を呼び出す行を除いてすべて同じままの「定型句」ですgetopt。 の後のプログラム名を変更し-n、 の後の短いオプション-o、 の後の長いオプションを指定する必要があります--long。値を取るオプションの後にはコロンを付けます。

set最後に、の代わりにだけがあるコードを見つけた場合eval set、それは BSD 用に書かれていますgetopt。 スタイルを使用するように変更する必要があります。eval setこのスタイルは の両方のバージョンで正常に動作しますgetoptが、プレーンな はsetGNU では正しく動作しませんgetopt

1実際、getoptsではksh93長い名前のオプションがサポートされていますが、このシェルは ほど頻繁には使用されませんbash。 ではzshzparseoptsこの機能を利用するために を使用します。

2技術的には、「GNU getopt」は誤った名称です。このバージョンは実際には GNU プロジェクトではなく Linux 用に作成されました。ただし、すべての GNU 規則に従っており、「GNU」という用語はgetopt一般的に使用されています (FreeBSD など)。

おすすめ記事