#条件付き判断
この章ではBashスクリプトの条件判定構文を紹介します。
構造体の場合
if
は最も一般的に使用される条件判定構造です。指定されたコマンドは、指定された条件が満たされた場合にのみ実行されます。その構文は次のとおりです。
if commands; then
commands
[elif commands; then
commands...]
[else
commands]
fi
このコマンドは、if
、elif
和else
の 3 つの部分に分かれています。このうち、最後の 2 つの部分はオプションです。
if キーワードの後に主な判定条件が続き、elif は主条件が成立しない場合に他の判定条件を追加する部分、else は全ての条件が成立しない場合に実行される部分です。
if test $USER = "foo"; then
echo "Hello foo."
else
echo "You are not foo."
fi
上記の例では、環境変数 $USER が foo と等しいかどうかが判定条件となっており、等しい場合は Hello foo. が出力され、そうでない場合はその他の内容が出力されます。
if と then を同じ行に記述する場合はセミコロンで区切る必要があります。セミコロンは Bash のコマンド区切り文字です。 2 行に記述することもできます。その場合、セミコロンは必要ありません。
if true
then
echo 'hello world'
fi
if false
then
echo 'it is false' # 本行不会执行
fi
上記の例では、true
とfalse
は 2 つの特別なコマンドであり、前者は操作が成功したことを意味し、後者は操作が失敗したことを意味します。 if true
はコマンド部分が常に実行されることを意味し、if false
はコマンド部分が決して実行されないことを意味します。
if 構造は複数行で記述するだけでなく、単一行で記述することもできます。
$ if true; then echo 'hello world'; fi
hello world
$ if false; then echo "It's true."; fi
なお、if キーワードの後にコマンドを続けることもできます。コマンドの実行成功(戻り値 0)は、判定条件が成立したことを意味します。
$ if echo 'hi'; then echo 'hello world'; fi
hi
hello world
上記のコマンドでは、if
の後にコマンドecho 'hi'
が続きます。コマンドが実行され、戻り値が 0
であれば、then
部分が実行されます。
if
の後には任意の数のコマンドを続けることができます。このとき、すべてのコマンドが実行されますが、最後のコマンドのみが true または false と判断されます。たとえそれまでのコマンドがすべて失敗しても、最後のコマンドが 0 を返していれば、then 部分が実行されます。
$ if false; true; then echo 'hello world'; fi
hello world
上記の例では、if
の後に 2 つのコマンド(false;true;
) があり、2 番目のコマンド (true
) はthen
部分を実行するかどうかを決定します。
複数の elif
部分が存在する可能性があります。
#!/bin/bash
echo -n "1 ~ 3 の数値を入力してください (両端も含みます) >"
read character
if [ "$character" = "1" ]; then
echo 1
elif [ "$character" = "2" ]; then
echo 2
elif [ "$character" = "3" ]; then
echo 3
else
echo 入力が要件を満たしていません
fi
上の例では3
を入力すると3回連続で判定されます。
テストコマンド
if
構造の判定条件は、一般的にtest
コマンドを使用し、3つの形式があります。
# 書き方その1
test expression
# 書き方2
[ expression ]
# 書き方3
[[ expression ]]
上記の 3 つの形式は同等ですが、3 番目の形式は通常の判断もサポートしますが、最初の 2 つはサポートしません。
上記のexpression
は式です。この式が true の場合、test
コマンドは正常に実行されます (戻り値は 0
)。式が false の場合、test
コマンドは失敗します (戻り値は 1
)。 2 番目と 3 番目の書き方では、[
と ]
と内部式の間にスペースが必要であることに注意してください。
$ test -f /etc/hosts
$ echo $?
0
$ [ -f /etc/hosts ]
$ echo $?
0
上記の例では、test
コマンドは 2 つの書き込み方法を使用して、/etc/hosts
ファイルが存在するかどうかを判断します。コマンドの実行後の戻り値は0
で、ファイルが存在することを示します。
実際、文字 [
は test
コマンドの短縮形であり、独立したコマンドと見なすことができます。これが、文字 [
の後にスペースが必要である理由を説明しています。
以下では、ファイルが存在するかどうかを判断するために、if
構造でtest
コマンドの 3 つの形式が使用されています。
# 書き方その1
if test -e /tmp/foo.txt ; then
echo "Found foo.txt"
fi
# 書き方2
if [ -e /tmp/foo.txt ] ; then
echo "Found foo.txt"
fi
# 書き方3
if [[ -e /tmp/foo.txt ]] ; then
echo "Found foo.txt"
fi
判定式
if
キーワードの後にはコマンドがあります。このコマンドは、test
コマンドまたは他のコマンドにすることができます。コマンドの戻り値が0
の場合は判定が成立し、それ以外の場合は不成立を意味する。これらのコマンドは主に戻り値を取得するために使用されるため、式とみなすことができます。
よく使われる判断表現としては以下のようなものがあります。
ファイル判定
ファイルの状態の判定には次の式が使用されます。
[ -a file ]
: ファイルが存在する場合はtrue
。[ -b file ]
: ファイルが存在し、ブロック (デバイス) ファイルである場合はtrue
。[ -c file ]
: ファイルが存在し、キャラクター (デバイス) ファイルである場合はtrue
。[ -d file ]
: ファイルが存在し、ディレクトリである場合はtrue
。[ -e file ]
: ファイルが存在する場合はtrue
。[ -f file ]
: ファイルが存在し、通常のファイルである場合はtrue
。[ -g file ]
: ファイルが存在し、グループ ID が設定されている場合はtrue
。[ -G file ]
: ファイルが存在し、有効なグループ ID に属している場合はtrue
。[ -h file ]
: ファイルが存在し、シンボリック リンクである場合はtrue
。[ -k file ]
: ファイルが存在し、そのスティッキー ビット
が設定されている場合はtrue
。[ -L file ]
: ファイルが存在し、シンボリック リンクである場合はtrue
。[ -N file ]
: ファイルが存在し、最後に読み込まれてから変更されている場合はtrue
。[ -O file ]
: ファイルが存在し、有効なユーザー ID に属している場合はtrue
。[ -p file ]
: ファイルが存在し、名前付きパイプである場合はtrue
。[ -r file ]
: ファイルが存在し、読み取り可能な場合はtrue
(現在のユーザーが読み取り権限を持っている)。[ -s file ]
: ファイルが存在し、その長さがゼロより大きい場合はtrue
。[ -S file ]
: ファイルが存在し、ネットワークソケットである場合はtrue
。[ -t fd ]
: fd がファイル記述子であり、端末にリダイレクトされる場合はtrue
。 これを使用して、標準入力/出力/エラーがリダイレクトされたかどうかを判断できます。[ -u file ]
: ファイルが存在し、setuid ビットが設定されている場合はtrue
。[ -w file ]
: ファイルが存在し、書き込み可能な場合はtrue
(現在のユーザーが書き込み権限を持っている)。[ -x file ]
: ファイルが存在し、実行可能である場合はtrue
(有効なユーザーは実行/検索権限を持っています)。[ FILE1 -nt FILE2 ]
: FILE1 が FILE2 よりも最近更新された場合、または FILE1 は存在するが FILE2 が存在しない場合は、true
。[ FILE1 -ot FILE2 ]
: FILE1 が FILE2 より古い場合、または FILE2 は存在するが FILE1 が存在しない場合はtrue
。[ FILE1 -ef FILE2 ]
: FILE1 と FILE2 が同じデバイスと i ノード番号を参照する場合はtrue
。
ここに例を示します。
#!/bin/bash
FILE=~/.bashrc
if [ -e "$FILE" ]; then
if [ -f "$FILE" ]; then
echo "$FILE is a regular file."
fi
if [ -d "$FILE" ]; then
echo "$FILE is a directory."
fi
if [ -r "$FILE" ]; then
echo "$FILE is readable."
fi
if [ -w "$FILE" ]; then
echo "$FILE is writable."
fi
if [ -x "$FILE" ]; then
echo "$FILE is executable/searchable."
fi
else
echo "$FILE does not exist"
exit 1
fi
上記のコードでは、変数 $FILE
が空になってエラーが発生するのを防ぐために、$FILE
を二重引用符で囲む必要があります。なぜなら、$FILE
が空の場合、[ -e $FILE ]
は[ -e ]
となり、trueと判断されるからです。 $FILE
をダブルクォートで囲むと、[ -e "$FILE" ]
が[ -e "" ]
となり偽と判定されます。
文字列判定
文字列の決定には次の式が使用されます。
[ string ]
:string
が空でない(長さが0より大きい)場合、trueと判断されます。[ -n string ]
: 文字列string
の長さがゼロより大きい場合は true。[ -z string ]
: 文字列string
の長さがゼロの場合は true。[ string1 = string2 ]
:string1
とstring2
が同じ場合は true。[ string1 == string2 ]
は[ string1 = string2 ]
と同等です。[ string1 != string2 ]
:string1
とstring2
が同じでない場合は true。[ string1 '>' string2 ]
: これは、辞書順でstring1
がstring2
の後に配置されている場合に当てはまります。[ string1 '<' string2 ]
: 辞書順でstring1
がstring2
より前に配置されている場合に当てはまります。
test
コマンド内の>
と<
は引用符で囲む(またはバックスラッシュでエスケープする)必要があることに注意してください。それ以外の場合、それらはシェルによってリダイレクト演算子として解釈されます。
ここに例を示します。
#!/bin/bash
ANSWER=maybe
if [ -z "$ANSWER" ]; then
echo "There is no answer." >&2
exit 1
fi
if [ "$ANSWER" = "yes" ]; then
echo "The answer is YES."
elif [ "$ANSWER" = "no" ]; then
echo "The answer is NO."
elif [ "$ANSWER" = "maybe" ]; then
echo "The answer is MAYBE."
else
echo "The answer is UNKNOWN."
fi
上記のコードでは、まず $ANSWER
文字列が空かどうかを確認します。空の場合、スクリプトを終了し、終了ステータスを1
に設定します。ここでのecho
コマンドは、エラー メッセージ応答がありません。
を標準エラーにリダイレクトすることに注意してください。これは、エラー メッセージを処理する一般的な方法です。 $ANSWER
文字列が空でない場合は、その値が yes
、no
、または maybe
に等しいかどうかを判断します。
文字列を判断するときは、変数を [ -n "$COUNT" ]
のように二重引用符で囲む必要があることに注意してください。そうしないと、変数が文字列に置き換えられた後、test
コマンドがエラーを報告し、次のメッセージが表示される可能性があります。パラメータが多すぎます。また、ダブルクォーテーションで囲まないと、変数が空の場合、コマンドは[-n]
となり、trueと判断されます。二重引用符で囲んだ場合、[ -n "" ]
は false と判断されます。
整数判定
整数を求めるには次の式を使用します。
[ integer1 -eq integer2 ]
:integer1
がinteger2
と等しい場合はtrue
。[ integer1 -ne integer2 ]
:integer1
がinteger2
と等しくない場合はtrue
。[ integer1 -le integer2 ]
:integer1
がinteger2
以下の場合はtrue
。[ integer1 -lt integer2 ]
:integer1
がinteger2
より小さい場合はtrue
。[ integer1 -ge integer2 ]
:integer1
がinteger2
以上の場合はtrue
。[ integer1 -gt integer2 ]
:integer1
がinteger2
より大きい場合はtrue
。
以下に使用例を示します。
#!/bin/bash
INT=-5
if [ -z "$INT" ]; then
echo "INT is empty." >&2
exit 1
fi
if [ $INT -eq 0 ]; then
echo "INT is zero."
else
if [ $INT -lt 0 ]; then
echo "INT is negative."
else
echo "INT is positive."
fi
if [ $((INT % 2)) -eq 0 ]; then
echo "INT is even."
else
echo "INT is odd."
fi
fi
上記の例では、まず変数 $INT
が空かどうかを判断し、次に変数 `0' かどうかを判断し、次に正か負かを判断し、最後に余りを求めて奇数か偶数かを判断します。
定期判定
判定形式 [[ expression ]]
は正規表現をサポートしています。
[[ string1 =~ regex ]]
上記の構文では、regex
は正規表現、=~
は正規の比較演算子です。
以下に例を示します。
#!/bin/bash
INT=-5
if [[ "$INT" =~ ^-?[0-9]+$ ]]; then
echo "INT is an integer."
exit 0
else
echo "INT is not an integer." >&2
exit 1
fi
上記のコードでは、まず変数 INT
の文字列形式が ^-?[0-9]+$
の規則的なパターンを満たすかどうかを判断します。満たしている場合は、それが整数であることを示します。
判定の論理演算をテストする
論理演算により、複数のテスト
判定式を組み合わせて、より複雑な判定を作成できます。 3 つの論理演算AND
、OR
、およびNOT
には、それぞれ独自の特殊記号があります。
AND
演算: シンボル&&
、パラメータ-a
も使用できます。OR
演算: 記号||
、パラメータ-o
も使用できます。NOT
操作: 記号!
。
以下は、整数が特定の範囲内にあるかどうかを判断するAND
の例です。
#!/bin/bash
MIN_VAL=1
MAX_VAL=100
INT=50
if [[ "$INT" =~ ^-?[0-9]+$ ]]; then
if [[ $INT -ge $MIN_VAL && $INT -le $MAX_VAL ]]; then
echo "$INT is within $MIN_VAL to $MAX_VAL."
else
echo "$INT is out of range."
fi
else
echo "INT is not an integer." >&2
exit 1
fi
上記の例では、$MIN_VAL以上
と$MAX_VAL
以下の2つの判定条件を&&
で接続しています。
否定演算子 !
を使用する場合は、括弧を使用してエスケープの範囲を決定するのが最善です。
if [ ! \( $INT -ge $MIN_VAL -a $INT -le $MAX_VAL \) ]; then
echo "$INT is outside $MIN_VAL to $MAX_VAL."
else
echo "$INT is in range."
fi
上記の例では、test
コマンド内で使用されている括弧は引用符で囲むかエスケープする必要があります。そうしないと、Bash によって解釈されます。
2 つの判定条件を結合するために -a
を使用するのは直感的ではありません。通常は、代わりに &&
を使用することをお勧めします。
if !([ $INT -ge $MIN_VAL ] && [ $INT -le $MAX_VAL ]); then
echo "$INT is outside $MIN_VAL to $MAX_VAL."
else
echo "$INT is in range."
fi
算術判定
Bash には、算術演算を判断するための算術条件として ((...))
も用意されています。
if ((3 > 2)); then
echo "true"
fi
上記のコードを実行すると、true
が出力されます。
なお、算術判定は test コマンドを使用する必要はなく、直接 ((...))
構造体を使用します。この構造体の戻り値によって、判定の信頼性が決まります。
算術計算の結果がゼロ以外の値であれば、判定は真となります。これはコマンドの戻り値とは全く逆になりますので注意してください。
$ if ((1)); then echo "It is true."; fi
It is true.
$ if ((0)); then echo "It is true."; else echo "it is false."; fi
It is false.
上記の例において、((1))
は判定が真であることを意味し、((0))
は判定が真でないことを意味する。
算術条件 ((...))
も変数の代入に使用できます。
$ if (( foo = 5 ));then echo "foo is $foo"; fi
foo is 5
上の例では、(( foo = 5 ))
は 2 つのことを実現します。まず変数foo
に5
を代入し、戻り値5
に基づいて条件が真であると判断します。
なお、代入文は等号の右側の値を返すので、0
を返すと偽と判断されます。
$ if (( foo = 0 ));then echo "It is true.";else echo "It is false."; fi
It is false.
以下は数値判定スクリプトを演算条件で書き換えたものです。
#!/bin/bash
INT=-5
if [[ "$INT" =~ ^-?[0-9]+$ ]]; then
if ((INT == 0)); then
echo "INT is zero."
else
if ((INT < 0)); then
echo "INT is negative."
else
echo "INT is positive."
fi
if (( ((INT % 2)) == 0)); then
echo "INT is even."
else
echo "INT is odd."
fi
fi
else
echo "INT is not an integer." >&2
exit 1
fi
算術式である限り、((...))
構文で使用できます。詳細については、Bash 算術演算
の章を参照してください。
一般的なコマンドの論理演算
if
構造が test
コマンドではなく、前のセクションの ((...))
算術演算などの通常のコマンドを使用している場合、または test
コマンドが通常のコマンドと混在している場合、 Bash を使用できます。 コマンド制御演算子 &&
(AND) および ||
(OR) は、複数のコマンドに対して論理演算を実行します。
$ command1 && command2
$ command1 || command2
&&
演算子の場合、command1
が最初に実行され、command2
は command1
が正常に実行された後にのみ実行されます。 ||
演算子の場合、command1
が最初に実行され、command2
は command1
が失敗した後にのみ実行されます。
$ mkdir temp && cd temp
上記のコマンドは、temp
という名前のディレクトリを作成します。実行が成功すると、2 番目のコマンドが実行されて、このディレクトリに入ります。
$ [ -d temp ] || mkdir temp
上記のコマンドは、ディレクトリ temp
が存在するかどうかをテストします。存在しない場合は、2 番目のコマンドが実行されてディレクトリが作成されます。この書き方は、スクリプト内のエラーを処理するのに非常に役立ちます。
[ ! -d temp ] && exit 1
上記のコマンドでは、temp
サブディレクトリが存在しない場合、スクリプトは終了し、戻り値は 1
になります。
以下は if と && を組み合わせた書き方です。
if [ condition ] && [ condition ]; then
command
fi
ここに例を示します。
#! /bin/bash
filename=$1
word1=$2
word2=$3
if grep $word1 $filename && grep $word2 $filename
then
echo "$word1 and $word2 are both in $filename."
fi
上記の例では、指定したファイル内に検索語word1
とword2
が存在する場合のみ、if
のコマンド部分が実行されます。
次の例は、&&
判定式を対応するif
構造に書き換える方法を示しています。
[[ -d "$dir_name" ]] && cd "$dir_name" && rm *
# に相当
if [[ ! -d "$dir_name" ]]; then
echo "No such directory: '$dir_name'" >&2
exit 1
fi
if ! cd "$dir_name"; then
echo "Cannot cd to '$dir_name'" >&2
exit 1
fi
if ! rm *; then
echo "File deletion failed. Check results" >&2
exit 1
fi
ケース構造
case
構造は複数の値の判定に使用され、各値に対応するコマンドを指定できます。これは複数の elif
を含む if
構造と同等ですが、より優れたセマンティクスを備えています。その構文は次のとおりです。
case expression in
pattern )
commands ;;
pattern )
commands ;;
...
イーサック
上記のコードでは、式
は式、パターン
は式の値またはパターンです。各項目は 2 つのセミコロン (;
) で終わります。
#!/bin/bash
echo -n "1 ~ 3 の数値を入力してください (両端も含みます) >"
read character
case $character in
1 ) echo 1
;;
2 ) echo 2
;;
3 ) echo 3
;;
* ) echo 入力が要件を満たしていません
esac
上記の例では、最後に一致するステートメントのパターンは*
です。このワイルドカードは他の文字と一致することができ、if
のelse
部分と同様に文字は入力されません。
別の例を示します。
#!/bin/bash
OS=$(uname -s)
case "$OS" in
FreeBSD) echo "This is FreeBSD" ;;
Darwin) echo "This is Mac OSX" ;;
AIX) echo "This is AIX" ;;
Minix) echo "This is Minix" ;;
Linux) echo "This is Linux" ;;
*) echo "Failed to identify this OS" ;;
esac
上の例では、現在のオペレーティング システムを特定します。
case
の一致パターンにはさまざまなワイルドカードを使用できます。以下にいくつかの例を示します。
a)
:a
と一致します。a|b)
:a
またはb
に一致します。[[:alpha:]])
: 単一の文字と一致します。???)
: 3 文字の単語と一致します。*.txt)
:.txt
の末尾と一致します。*)
: 任意の入力と一致します。通常はcase
構造の最後のパターンとして一致します。
#!/bin/bash
echo -n "文字または数字を入力してください > "
read character
case $character in
[[: lower:]] [[:upper:]] ) echo "$character という文字が入力されました"
;;
[0-9] ) echo "数字 $character が入力されました"
;;
* ) echo "入力が要件を満たしていません"
esac
上記の例では、ワイルドカード文字 [[:lower:]] | [[:upper:]]
が文字の一致に使用され、 [0-9]
が数字の一致に使用されます。
Bash 4.0 より前では、case
構造体は 1 つの条件にのみ一致すると、case
構造体は終了します。 Bash 4.0 以降では、複数の条件を一致させることができます。この場合、各条件ブロックは ;;&
で終了できます。
#!/bin/bash
# test.sh
read -n 1 -p "Type a character > "
echo
case $REPLY in
[[:upper:]]) echo "'$REPLY' is upper case." ;;&
[[:lower:]]) echo "'$REPLY' is lower case." ;;&
[[:alpha:]]) echo "'$REPLY' is alphabetic." ;;&
[[:digit:]]) echo "'$REPLY' is a digit." ;;&
[[:graph:]]) echo "'$REPLY' is a visible character." ;;&
[[:punct:]]) echo "'$REPLY' is a punctuation symbol." ;;&
[[:space:]]) echo "'$REPLY' is a whitespace character." ;;&
[[:xdigit:]]) echo "'$REPLY' is a hexadecimal digit." ;;&
esac
上記のスクリプトを実行すると、次の結果が得られます。
$ test.sh
Type a character > a
'a' is lower case.
'a' is alphabetic.
'a' is a visible character.
'a' is a hexadecimal digit.
条件文の最後に ;;& を追加すると、条件が一致した後、 case
構造が終了せずに、次の条件を判断し続けていることがわかります。
参考リンク
- Linux コマンド ライン、William Shotts
作者: wangdoc
アドレス: https://wangdoc.com/
ライセンス: クリエイティブ・コモンズ 3.0