1. ホーム
  2. スクリプト・コラム
  3. リナックスシェル

コマンドライン引数を読み込むシェルスクリプトの実装

2022-01-09 04:54:07

前提

シェルプログラムを作成する際に、コマンドライン引数を処理する必要がある場合が多い

オプションと引数

次のようなコマンドラインです。

. /test.sh -f config.conf -v --prefix=

-fはオプションで、引数は1つ、つまりconfig.confです。
-vもオプションですが、引数を取りません。
-prefixはいわゆるロングオプションで、オプション自体が1文字以上あり、さらに等号で結ばれた引数を取りますが、もちろんこれは必須ではありません。
/home は --prefix の後に直接書くことができます(-prefix/home)。
その他の制限事項については、後ほど指定します。

I. 手動処理方法(検証済み)

手動処理法では、上のコマンドラインに代表されるように、まず知っておくべき変数がいくつかあります。

$0: . /test.sh、つまりコマンドそのもの、これはc/c++のargv[0]に相当します。
$1: -f、第1引数です。
2: config.conf
3ドル、4ドル ... : アナログです。
$#: コマンド自身を含まない引数の数で、上の例では $# は 4 です。
$@: コマンド自身を含まない引数リスト、例:-f config.conf -v --prefix=
$*: $@ と同じですが、「$*」と「$@」(引用符で囲む)は同じではなく、「$*」はすべての引数を文字列として解釈し、「$@」は引数の配列になります。

プレ #! /binfor arg in "$*" do echo $arg done for arg in "$@" do echo $arg done

実行します。/test.sh -f config.conf -n 10 と実行すると、以下のように表示されます。

# これは、"$*" の出力です。
-f config.conf -n 10 

#以下は、$@の出力です。
-f  
config.conf
-n
10

そこで、これらの変数を処理する方法として、手動処理があります。手動処理はコマンドラインのどこに引数を渡すかに大きく依存するため、一般的にはより単純な引数にのみ使用されます。

例えば

. /test.sh 10

を使用することはほとんどありません。/test -n 10 をオプションで使用します。

典型的な使い方は

#! /binif [ x$1 ! = x ]
then
  #... with argument logic
else
then
  #... No parameter logic
fi

なぜ x$1 ! = x を使って比較するのか?(xはどんな文字でも構いません)
このように比較することをイメージしてください。

if [ -n $1 ] #$1 is not empty

しかし、ユーザーが引数を渡さないときに$1が空だと、[ -n ]となるので、比較するためのヘルパー文字列を追加する必要があります。
手動の方法でも単純なニーズはほとんど満たせますし、shiftと併用すると強力に構築できますが、複雑なオプションを扱いたい場合は、以下の2つの方法をお勧めします。

II.ゲトオプティクス

コマンドライン引数の処理も同様ですが、複雑な問題で、そのために c は getopt/getopt_long のような関数を提供しています。

c++のboostがオプションライブラリを提供し、シェルではgetoptsとgetoptがこれを処理する。

getoptsとgetoptは似て非なるもので、getoptはスタンドアロン実行ファイル、getoptsはbashでビルトインされます。

  •  . /test.sh -a -b -c : 短いオプション、各オプションに引数は必要ありません。
  • . /test.sh -abc : 短いオプション、前の方法と同じ効果で、すべてのオプションを一緒に書くだけです。
  • . /test.sh -a args -b -c : 短いオプションで、-a は引数を取り、-b -c は引数を取らない。
  • . /test.sh --a-long=args --b-long : ロングオプション {を使用します。

まず、長いオプションに対応していないgetoptsを見てみましょう。

getoptsの使い方はとても簡単です。

#test.sh
#! /binwhile getopts "a:bc" arg # A colon after an option means that the option takes an argument
do
        case $arg in
             a)
                echo "a's arg:$optarg" # arguments are in $optarg

             b)
                echo "b"

             c)
                echo "c"

             ?)  # When there is an unrecognized option arg for ?
            echo "unkonw argument"
        exit 1

        esac
done


使用できるようになりました。

. /test.sh -a arg -b -c

または

. /test.sh -a arg -bc


を読み込むことができます。

ほとんどのスクリプトはこの関数で大丈夫ですが、長いオプションやオプションのパラメータをサポートする必要がある場合は、getoptを使う必要があることをお伝えしておきます。
getoptに付属する例の1つは、次のとおりです。

#! /bin# a small example program for using the new getopt(1) program.
# this program will only work with bash(1)
# an similar program using the tcsh(1) script language can be found
# as parse.tcsh
# example input and output (from the bash prompt):
# . /parse.bash -a par1 'another arg' --c-long 'wow!*\?' -cmore -b " very long "
# option a
# option c, no argument
# option c, argument `more'
# option b, argument ` very long '
# remaining arguments:
# --> `par1'
# --> `another arg'
# --> `wow!*\?'
# note that we use `"$@"' to let each command-line parameter expand to a
# The quotes around `$@' are essential!
# we need temp as the `eval set --' would nuke the return value of getopt.
# -o means short option, two colons means the option has an optional argument, the optional argument must be close to the option
#carg instead of --c arg
#--long indicates a long option
#"$@" explained above
# -n: message in case of error
# --: An example is better understood.
# We want to create a directory with the name "-f" What would you do?
# mkdir -f # doesn't work because -f will be parsed by mkdir as an option, then you can use
# mkdir -- -f so that -f is not used as an option.
temp=`getopt -o ab:c:: --long a-long,b-long:,c-long:: \
     -n 'example.bash' -- "$@"`
if [ $? ! = 0 ] ; then echo "terminating... " >&2 ; exit 1 ; fi
# note the quotes around `$temp': they are essential!
#set will rearrange the order of the arguments, i.e. change the values of $1,$2... The values of $n, which are rearranged in getopt
eval set -- "$temp"
# After the getopt process, the following process the specific options.
while true ; do
        case "$1" in
                -a|--a-long) echo "option a" ; shift ;;
                -b|--b-long) echo "option b, argument \`$2'" ; shift 2 ;;
                -c|--c-long)
                        # c has an optional argument. as we are in quoted mode,
                        # an empty parameter will be generated if its optional
                        # argument is not found.
                        case "$2" in
                                "") echo "option c, no argument"; shift 2 ;;
                                *) echo "option c, argument \`$2'" ; shift 2 ;;
                        esac ;;
                --) shift ; break ;;
                *) echo "internal error!" ; exit 1 ;;
        esac
done
echo "remaining arguments:"
for arg do
   echo '--> '"\`$arg'" ;
done

例えば、以下のように使用します。

. /test -a -b arg arg1 -c

ご覧の通り、コマンドラインにはarg1という引数が追加されており、getoptとsetの後、コマンドラインが

-a -b arg -c -- arg1

1点は-a、2点は-b、3点はarg、4点は-c、5点は--で、最後に余分なarg1が付く。

III. 概要
/{br

小さなスクリプトは通常手動で処理するのに十分です。getoptsはほとんどのケースを処理できます。getoptはより複雑でより強力です。

コマンドライン引数を読むシェルスクリプトの記事は以上です。コマンドライン引数を読むシェルに関するより詳しい情報は、過去の記事を検索するか、以下の関連記事を引き続きご覧ください。