#条件付き判断

この章ではBashスクリプトの条件判定構文を紹介します。

構造体の場合

if は最も一般的に使用される条件判定構造です。指定されたコマンドは、指定された条件が満たされた場合にのみ実行されます。その構文は次のとおりです。

if commands; then
  commands
[elif commands; then
  commands...]
[else
  commands]
fi

このコマンドは、ifelifelse の 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

上記の例では、truefalseは 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コマンドまたは他のコマンドにすることができます。コマンドの戻り値がの場合は判定が成立し、それ以外の場合は不成立を意味する。これらのコマンドは主に戻り値を取得するために使用されるため、式とみなすことができます。

よく使われる判断表現としては以下のようなものがあります。

ファイル判定

ファイルの状態の判定には次の式が使用されます。

  • [ -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 ]: string1string2 が同じ場合は true。
  • [ string1 == string2 ][ string1 = string2 ] と同等です。
  • [ string1 != string2 ]: string1string2 が同じでない場合は true。
  • [ string1 '>' string2 ]: これは、辞書順で string1string2 の後に配置されている場合に当てはまります。
  • [ string1 '<' string2 ]: 辞書順で string1string2 より前に配置されている場合に当てはまります。

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 文字列が空でない場合は、その値が yesno、または maybe に等しいかどうかを判断します。

文字列を判断するときは、変数を [ -n "$COUNT" ] のように二重引用符で囲む必要があることに注意してください。そうしないと、変数が文字列に置き換えられた後、test コマンドがエラーを報告し、次のメッセージが表示される可能性があります。パラメータが多すぎます。また、ダブルクォーテーションで囲まないと、変数が空の場合、コマンドは[-n]となり、trueと判断されます。二重引用符で囲んだ場合、[ -n "" ] は false と判断されます。

整数判定

整数を求めるには次の式を使用します。

  • [ integer1 -eq integer2 ]: integer1integer2 と等しい場合は true
  • [ integer1 -ne integer2 ]: integer1integer2 と等しくない場合は true
  • [ integer1 -le integer2 ]: integer1integer2 以下の場合は true
  • [ integer1 -lt integer2 ]: integer1integer2 より小さい場合は true
  • [ integer1 -ge integer2 ]: integer1integer2 以上の場合は true
  • [ integer1 -gt integer2 ]: integer1integer2 より大きい場合は 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 つの論理演算ANDOR、および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 つのことを実現します。まず変数foo5を代入し、戻り値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 が最初に実行され、command2command1 が正常に実行された後にのみ実行されます。 || 演算子の場合、command1 が最初に実行され、command2command1 が失敗した後にのみ実行されます。

$ 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

上記の例では、指定したファイル内に検索語word1word2が存在する場合のみ、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

上記の例では、最後に一致するステートメントのパターンは*です。このワイルドカードは他の文字と一致することができ、ifelse部分と同様に文字は入力されません。

別の例を示します。

#!/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 構造が終了せずに、次の条件を判断し続けていることがわかります。

参考リンク


作者: wangdoc

アドレス: https://wangdoc.com/

ライセンス: クリエイティブ・コモンズ 3.0