bash プログラミング:複合コマンド

複合コマンドとは複数のコマンドを一まとめにしたり、算術評価式や正規表現を行う方法です。


サブシェル

サブシェルは複数のコマンドを1つのコマンドとして扱うもので、exit コマンドで値を返すことが出来ます。また、シェルとして実行される関係上、パイプなどの処理も行えます。

サブシェルを使用する場合は括弧「()」で囲む事で使用できます。

感覚的には子プロセスで実行する感じですが、実際に子プロセスで実行されるかどうかはバージョンなどによって異なります。

実際に子プロセスで実行されているかどうかは、次の方法で確認できます。

$ echo “シェルのPID: $$”; ( echo “サブシェルのPID: $$” )
シェルのPID: 471403
サブシェルのPID: 471403
$

では実際の使い方を見ていきます。

複数のコマンドを実行

$ ( echo “3”; echo “2”; echo “1” ) | cat
3
2
1
$

この例では複数のコマンドを実行し、1つのコマンドとして「cat」へパイプしています。

標準エラー出力へのリダイレクト

$ ( echo “エラーです” > /dev/stderr )
エラーです
$

サブシェル内でのパイプ

$ ( echo “ABC” | grep -i abc )
ABC
$

最後のコマンドの終了ステータスで判定。(「grep -i」で大文字/小文字を無視)

$ if ( echo “ABC” | grep -i abc ); then echo “Match”; else echo “NOT Match”; fi
ABC
Match
$

終了ステータスで判定。(「exit」)

$ if ( echo “ABC” | grep -i abc ; exit $? ); then echo “Match”; else echo “NOT Match”; fi
ABC
Match
$

サブシェルの標準出力を文字列化(「$」を付けると文字列化する)

$ echo “$( echo ‘AB’; echo ‘C’; echo ‘D’ )”
AB
C
D
$

グループコマンド

グループコマンドはサブシェルと同様に複数のコマンドを1つのコマンドとして扱うものですが、exit コマンドで値を返すことが出来ません。また、内包するコマンドは必ず「;」(セミコロン)で終了する必要があるなど制約も多く、グループコマンドで行える事はサブシェルでも行えます。

グループコマンドを使用する場合は波括弧「{}」で囲む事で使用できます。

複数のコマンドを実行

$ { echo “3”; echo “2”; echo “1”; } | cat
3
2
1
$

この例では複数のコマンドを実行し、1つのコマンドとして「cat」へパイプしています。

標準エラー出力へのリダイレクト

$ { echo “エラーです” > /dev/stderr ; }
エラーです
$

グループコマンド内でのパイプ

$ { echo “ABC” | grep -i abc ; }
ABC
$

最後のコマンドの終了ステータスで判定。(「grep -i」で大文字/小文字を無視)

$ if { echo “ABC” | grep -i abc ; }; then echo “Match”; else echo “NOT Match”; fi
ABC
Match
$

算術式評価

整数の計算などに用います。(浮動小数点は扱えません)

算術式評価を使用する場合は2重括弧「(())」で囲む事で使用できます。

算術式は以下の通りです。

  • id++ id–
    変数を評価し、その後 increment (加算)/ decrement (減算) する。
  • ++id –id
    変数を increment (加算) / decrement (減算) してから評価する。
  • – +
    単項式の負と正
  • ! ~
    論理的否定とビット単位の否定
  • **
    指数 (累乗)
  • * / %
    乗算、除算、剰余
  • + –
    加算と減算
  • << >>
    左ビットシフトと右ビットシフト
  • <= >= < >
    比較
  • == !=
    等値と非等値
  • &
    ビット単位の AND
  • ^
    ビット単位の排他的 OR
  • |
    ビット単位の OR
  • &&
    論理的 AND
  • ||
    論理的 OR
  • expr?expr:expr
    条件付き実行(三項演算子)
  • = *= /= %= += -= <<= >>= &= ^= |=
    代入
  • expr1 , expr2
    コンマ

計算と代入

$ (( value = 1 + 2 + 3 )); echo “value is ${value}”;
value is 6
$

等価判定

$ if (( 2 >= 1 )); then echo “BIG”; else echo “SMALL”; fi
BIG
$

算術結果を文字列化(「$」を付けると文字列化する)

$ echo $(( 1 + 2 + 3 ));
6
$

条件式評価

条件式評価は通常文字列に対して行います。

使用できる評価式は「==」「!=」「=~」です。

「==」と「!=」はパターンマッチングです。

「=~」は正規表現です。

これらの比較では「nocasematch」が設定されている場合には、大文字/小文字は無視されます。

条件式評価を使用する場合は2重角括弧「[[]]」で囲む事で使用できます。

大文字/小文字は無視

「shopt -s nocasematch」で設定、「shopt -u nocasematch」で解除される。

$ shopt -s nocasematch
$ if [[ “ABC” = abc ]]; then echo “マッチ”; else echo “アンマッチ”; fi
マッチ
$ shopt -u nocasematch
$ if [[ “ABC” = abc ]]; then echo “マッチ”; else echo “アンマッチ”; fi
アンマッチ
$ shopt -s nocasematch
$ if [[ “ABC” =~ abc ]]; then echo “マッチ”; else echo “アンマッチ”; fi
マッチ
$ shopt -u nocasematch
$ if [[ “ABC” =~ abc ]]; then echo “マッチ”; else echo “アンマッチ”; fi
アンマッチ
$

パターンマッチング

パターンに含まれる文字のうち、以下の特殊パターン文字以外の文字は、 自分自身にマッチしま
す。NUL 文字がパターン中に現われてはいけません。 バックスラッシュは直後の文字をエスケープ
します。 このバックスラッシュは、マッチングでは無視されます 特殊パターン文字をその文字その
ものにマッチさせるためには、 クォートしなければなりません。

特殊パターン文字は以下の意味を持っています

  • *
    空文字列を含む、任意の文字列にマッチします。
  • ?
    任意の 1 文字にマッチします。
  • […]
    括られた文字のうち任意の 1 文字にマッチします。 2 つの文字の間にハイフンを入 れたものは、 範囲表現を表します。 ソート順で両端を含む 2 つの文字の間にある任意の 1 文字と マッチします。 ソートには現在のロケールの 照合順序と文字セットが用いられます。 [ の次の文字が ! または ^ ならば、括られた文字に含まれない任意の 1 文字にマッチします。 範囲 表現における文字のソート順は、 現在のロケール (およびシェル変数 LC_COLLATE が指定されていればその値) によって決定されます。 - は、文字集合の最初または 最後の文字として含めると マッチングの対象にできます。 ] は、文字集合の最初の 文字として含めるとマッチングの対象にできます。 [ と ] の間では、文字クラスを指定できます。 指定には [:class:] という記法を使います。ここで class は POSIX 標準で定義されている以 下のクラスのいずれかです: alnum alpha ascii blank cntrl digit graph lower print punct space upper word xdigit 文字クラスは、そのクラスの属する任意の文字にマッチします。 文字クラス word は、文字、数字、_ にマッチします。 [ と ] の間では、等値クラスを指定できます。 指定には [=c=] という記法を使います。 これは現在のロケールにおける定義において c と同 じ 照合重み (collation weight) を持つ全ての文字にマッチします。 [ と ] の間では、[.symbol.] という記述は照合シンボル symbol にマッチします。
$ if [[ “ABC” == A* ]]; then echo “マッチ”; else echo “アンマッチ”; fi
マッチ
$ if [[ “ABC” == A?C ]]; then echo “マッチ”; else echo “アンマッチ”; fi
マッチ
$ if [[ “ABC” == A[BD]C ]]; then echo “マッチ”; else echo “アンマッチ”; fi
マッチ
$ if [[ “ABC” == A[B-C]C ]]; then echo “マッチ”; else echo “アンマッチ”; fi
マッチ
$ if [[ “ABC” == A[!X]C ]]; then echo “マッチ”; else echo “アンマッチ”; fi
マッチ
$ if [[ “ABC” == A[^X]C ]]; then echo “マッチ”; else echo “アンマッチ”; fi
マッチ
$ if [[ “ABC” == A[[:alnum:]]C ]]; then echo “マッチ”; else echo “アンマッチ”; fi
マッチ
$

正規表現

=~ を使うと、右辺の文字列は拡張正規表現とみなされ、それに従ってマッチングが行われます。 文字列がパターンにマッチすれば返り値は 0 であり、 マッチしなければ返り値は 1 になります。 正規表現が文法的に誤っていれば、条件式の返り値は 2 になります。 シェルオプション nocasematch が有効であれば、 アルファベットの大文字と小文字を考慮せずにマッチングが行われます。 パターン中のどの部分でも、クォート(シングルコーテーションやダブルコーテーションで囲む)することで、 ただの文字列としてマッチングさせることができます。 正規表現中の括弧による部分式にマッチした部分文字列は、配列変数 BASH_REMATCH に保存されます。 BASH_REMATCH のインデックス 0 の要素は、文字列のうち正規表現全体にマッチした部分になります。BASH_REMATCH のインデックス n の要素は、文字列のうち、 正規表現中の n 番目の括弧による部分式にマッチした部分になります。

正規表現部分は全体をクォート(シングルコーテーションやダブルコーテーションで囲む)すると、単純な文字列比較になります。

$ if [[ “ABC” =~ ^A..$ ]]; then echo “マッチ”; else echo “アンマッチ”; fi
マッチ
$ echo ${BASH_REMATCH[0]};
ABC
$ if [[ “ABC” =~ ^A(.).$ ]]; then echo “マッチ”; else echo “アンマッチ”; fi
マッチ
$ echo ${BASH_REMATCH[0]};
ABC
$ echo ${BASH_REMATCH[1]};
B
$

クォート

$ if [[ “ABC” =~ ^[A-B]’B’.$ ]]; then echo “マッチ”; else echo “アンマッチ”; fi
マッチ
$ if [[ “ABC” =~ ‘ABC’ ]]; then echo “マッチ”; else echo “アンマッチ”; fi
マッチ
$

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です