引数ありまたは引数なしで動作するスクリプトを作成する方法は?


13

次のようなbashスクリプトがあります。

#!/bin/bash

if [ $1 = "--test" ] || [ $1 = "-t" ]; then
    echo "Testing..."
    testing="y"

else
    testing="n"
    echo "Not testing."

fi

だから私ができるようにしたいことは、./script --testまたは./script -tで実行するだけでなく、引数なしで(ちょうど./script)だけでなく、現在のコードでそれを行う場合、出力はただです:

./script: line 3: [: =: unary operator expected
./script: line 3: [: =: unary operator expected
Not testing.

それでは、引数なしで実行するとelseエラーをスローせずに実行されるようにプログラムするにはどうすればよいですか?私は何を間違えていますか?


1
小切手を二重括弧で囲む必要があります。[[]]
テランス


@Terranceは必要ありません、bashをターゲットにする場合は使用する必要があります。
ルスラン

回答:


1

シェルスクリプトで長いオプションを使用する「適切な方法」は、getopt GNUユーティリティを使用することです。bash組み込みのgetoptsもありますが、などの短いオプションのみが許可されます-tgetopt使用法のいくつかの例をここで見つけることができます

以下は、あなたの質問にどのようにアプローチするかを示すスクリプトです。ほとんどの手順の説明は、スクリプト自体にコメントとして追加されます。

#!/bin/bash

# GNU getopt allows long options. Letters in -o correspond to
# comma-separated list given in --long.

opts=$(getopt -o t --long test -- "$*")
test $? -ne 0 && exit 2 # error happened

set -- $opts # some would prefer using eval set -- "$opts"
# if theres -- as first argument, the script is called without
# option flags
if [ "$1" = "--"  ]; then
    echo "Not testing"
    testing="n"
    # Here we exit, and avoid ever getting to argument parsing loop
    # A more practical case would be to call a function here
    # that performs for no options case
    exit 1
fi

# Although this question asks only for one 
# option flag, the proper use of getopt is with while loop
# That's why it's done so - to show proper form.
while true; do
    case "$1" in
        # spaces are important in the case definition
        -t | --test ) testing="y"; echo "Testing" ;;
    esac
    # Loop will terminate if there's no more  
    # positional parameters to shift
    shift  || break
done

echo "Out of loop"

いくつかの簡略化とコメントの削除により、これを次のように要約できます。

#!/bin/bash
opts=$(getopt -o t --long test -- "$*")
test $? -ne 0 && exit 2 # error happened
set -- $opts    
case "$1" in
    -t | --test ) testing="y"; echo "Testing";;
    --) testing="n"; echo "Not testing";;
esac

16

いくつかの方法; 最も明白な2つは次のとおりです。

  • プット $1二重引用符で:if [ "$1" = "--test" ]
  • $#を使用して引数の数を確認します

さらに良いことに、getoptsを使用します。


2
getoptsを使用するための+1。それが最善の方法です。
セス

3
別の一般的なイディオムは[ "x$1" = "x--test" ][コマンドの有効な演算子であるコマンドライン引数を再度防御することです。(これが[[ ]]優先されるもう1つの理由です。組み込みコマンドだけでなく、シェル構文の一部です。)
Peter Cordes

7

if条件内で変数を引用する必要があります。交換:

if [ $1 = "--test" ] || [ $1 = "-t" ]; then

で:

if [ "$1" = '--test' ] || [ "$1" = '-t' ]; then  

その後、それは動作します:

➜〜
. /test.sh テストしていません。

常に変数を二重引用符で囲んでください!


単一引用符は必須ですか、それとも二重引用符を代わりに使用できますか?

正確な答えは、使用しているシェルによって異なりますが、質問からは、Bourneシェルの子孫を使用していると思います。単一引用符と二重引用符の主な違いは、変数の展開が二重引用符内で行われますが、単一引用符内では行われないことです:$ FOO = bar $ echo "$ FOO" bar $ echo '$ FOO' $ FOO(hmm .... can ' tコメントのコードをフォーマット...)
JayEye

@ParanoidPanda単一引用符を使用する必要はありませんが、文字列リテラルで使用することをお勧めします。こうすることで、文字列データに記号bashが拡張したい場合でも、後で驚くことはありません。
セス

@ParanoidPanda何@JayEyeは言った:単一引用符では何の変数の拡張が存在しないfoo=bar echo '$foo'印刷物$fooを出力するが、foo=bar echo "$foo"印刷物barの代わりに。
EKons

4

bashを使用しています。Bashの優れた代替手段は[次のとおり[[です。を使用すると[[、引用について心配する必要はありません。また[、以下の適切なサポートを含め、より多くの演算子を取得できます||

if [[ $1 = "--test" || $1 = "-t" ]]
then
    echo "Testing..."
    testing="y"
else
    testing="n"
    echo "Not testing."
fi

または、正規表現を使用できます。

if [[ $1 =~ ^(--test|-t) ]]
then
    echo "Testing..."
    testing="y"
else
    testing="n"
    echo "Not testing."
fi

もちろん、常に適切な引用を行う必要がありますが、[bashを使用するときに固執する理由はありません。


3

引数が存在するかどうかをテストするには(一般的に、それに応じてアクションを実行します)これは動作するはずです:

#!/bin/bash

check=$1

if [ -z "$check" ]; then
  echo "no arguments"
else echo "there was an argument"
fi

どうして[ -z "$1" ]
EKons

@ΈρικΚωνσταντόπουλοςこの場合、確かに、しかし通常はスクリプトでは、変数を1回呼び出して、手順でそれを参照します。サンプルにプロトコルを保持することにしました。
ジェイコブVlijm

このサンプルでは$checkshiftsの前に1回使用するようですので、$1代わりに使用することをお勧めします。
EKons

@ΈρικΚωνσταντόπουλοςもちろん、前述したように、スクリプトで使用されるサンプルではありません。スクリプトでは、同じことを何度も繰り返し行うのは悪い習慣です。
ジェイコブVlijm

私はあなたの意味を理解していますが、#!サンプルを提供する際にシバンを使用しない方が良い習慣です(コードの外側でシェルを記述する必要があります)。
EKons
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.