Bash Basics | Basicsトップページ | トップページ

算術式評価

シェルでは複合コマンド((組込コマンドlet、および算術式展開で算術式が評価されます。
算術式の評価は固定長の整数(内部的にはintmax_tであり、ほとんどのシステムでは64ビットの符号付き整数)として行われますが、オーバーフローのチェックは行われないのでご注意ください。
0での除算はトラップされ、エラーとしてのフラグが立てられます。

bashの算術式は演算子とオペランド(被演算子)の組み合わせで構成されます。
以下の表のオペランドが使用できます。

種類説明
10進数リテラル10進数表記の数字です。
1から9で始まる数字の並びは10進数のリテラルと解釈されます。
$ echo $((1))
1
$ echo $((100))
100
$ echo $((1234))
1234
8進数リテラル8進数表記の数字です。
0で始まる数字の並びは8進数のリテラルと解釈されます。
$ echo 0755
493
$ echo $((0100))
64
$ echo $((034))
28
16進数リテラル16進数表記の数字です。
0xまたは、0Xで始まる数字とAからF(およびaからf)の文字の並びは16進数のリテラルと解釈されます。
$ echo $((0x3F))
63
$ echo $((0xFF))
255
$ echo $((0x2a))
42
n進数リテラル任意のn進数表記の数字です。
n#(nは2以上64以下の数字)で始まる数字とAからF、およびaからfと@、_の文字の並びはn進数のリテラルと解釈されます。
nが36以下の場合、aからz(およびAからZ)の文字が10から35の値を意味します。nが36より大きい場合、英小文字aからzの文字が10から35を意味し、英大文字AからZの文字が36から61を意味します。62は@、63は_で記述します。
$ echo $((60#2X))
179
$ echo $((24#ja))
466
$ echo $((30#2c))
72
変数任意の変数の名前を算術式内で記述することができます。
未定義の変数や値が空の変数は0として評価されます。
$ x=123; y=0xff; echo $((x)) $((y)) $((z))
123 255 0
$ var=(123 0xff 0755); echo $((var[1]))
255
$ declare -A price=([apple]=450 [orange]=400); echo $((var[apple]))
450

シェルで使用できる演算子とその優先度、結合規則はbashの拡張を除き、C言語と同じです。
以下の表に同じ優先度ごとに演算子をまとめて、優先度の高いものから順に説明します。
優先順位結合規則演算子名前説明
1左から右変数名++後置インクリメント演算子変数の値を評価し、その後、変数の値をインクリメント(値を1加算)します。
$ x=10; echo $((x++)), $x
10, 11
変数名--後置デクリメント変数の値を評価し、その後、変数の値をデクリメント(値を1減算)します。
$ x=10; echo $((x--)), $x
10, 9
2右から左++変数名前置インクリメント演算子変数の値をインクリメント(値を1加算)し、その後、変数の値を評価します。
$ x=10; echo $((++x)), $x
11, 11
--変数名前置デクリメント変数の値をデクリメント(値を1減算)し、その後、変数の値を評価します。
$ x=10; echo $((--x)), $x
9, 9
3右から左-負の符号単項式の値を負として評価(符号を反転)します。
$ x=10; y=-5; echo $((-x)), $((-y))
-10, 5
+正の符号単項式の値を正として評価します。
$ x=10; y=-5; echo $((+x)), $((+y))
10, -5
4右から左!論理的否定算術式を論理否定(算術式の評価結果が0以外であれば0を返し、算術式の結果が0であれば1を返します)します。
$ x=10; y=0; echo $((!x)), $((!y))
0, 1
~1の補数演算子算術式の値のビット単位の補数を生成(ビットを反転)します。
$ x=0x3f4d; printf "%016x\n%016x\n" $((x)) $((~x))
0000000000003f4d
ffffffffffffc0b2
$ x=1; printf "%016x\n%016x\n" $((x)) $((~x))
0000000000000001
fffffffffffffffe
5右から左**指数(累乗)bashの拡張で演算子の左辺の値を右辺の値で累乗します。
$ x=10; y=5; echo $((x**y))
100000
$ x=2; y=4; echo $((x**y))
16
$ x=3; y=4; echo $((x**y))
81
6左から右*乗算演算子の左辺の値を右辺の値で乗算します。
$ x=3; y=4; echo $((x*y))
12
/除算演算子の左辺の値を右辺の値で除算します。
$ x=20; y=4; echo $((x/y))
5
$ x=23; y=4; echo $((x/y))
5
%剰余演算子の左辺の値を右辺の値で除算した剰余を演算します。
$ x=20; y=4; echo $((x%y))
0
$ x=23; y=4; echo $((x%y))
3
7左から右+加算演算子の左辺の値に右辺の値を加算します。
$ x=3; y=4; echo $((x+y))
7
-減算演算子の左辺の値から右辺の値を減算します。
$ x=3; y=4; echo $((x-y))
-1
8左から右<<左ビットシフト演算子の左辺の値を右辺の値のビット数分だけ左にシフトします。
$ x=47; echo "obase=2; $((x))" | bc; echo "obase=2; $((x<<2))" | bc;
101111
10111100
>>右ビットシフト演算子の左辺の値を右辺の値のビット数分だけ右にシフトします。
負の値を右ビットシフトした場合、符号拡張が行われます。
$ x=47; echo "obase=2; $((x))" | bc; echo "obase=2; $((x>>2))" | bc;
101111
1011
$ x=-10; printf "%016x\n%016x\n" $((x)) $((x>>8))
fffffffffffffff6
ffffffffffffffff
9左から右<=大小比較演算子の左辺の値に対して右辺の値が等しいか大きければ1を返し、小さければ0を返します。
$ x=7; y=4; echo $((x<=y))
0
$ x=7; y=10; echo $((x<=y))
1
>=大小比較演算子の左辺の値に対して右辺の値が等しいか小さければ1を返し、大きければ0を返します。
$ x=7; y=7; echo $((x>=y))
1
$ x=7; y=10; echo $((x>=y))
0
<大小比較演算子の左辺の値に対して右辺の値が大きければ1を返し、等しいか小さければ0を返します。
$ x=7; y=7; echo $((x<y))
0
$ x=7; y=10; echo $((x<y))
1
>大小比較演算子の左辺の値に対して右辺の値が小さければ1を返し、等しいか大きければ0を返します。
$ x=7; y=7; echo $((x>y))
0
$ x=10; y=5; echo $((x>y))
1
10左から右==等値比較演算子の左辺の値と右辺の値が等しければ1を返し、等しくなければ0を返します。
$ x=7; y=7; echo $((x==y))
1
$ x=10; y=5; echo $((x==y))
0
!=非等値比較演算子の左辺の値と右辺の値が等しくなければ1を返し、等しければ0を返します。
$ x=7; y=7; echo $((x!=y))
0
$ x=10; y=5; echo $((x!=y))
1
11左から右&ビット単位のAND演算子の左辺の値を右辺の値のビットと比較し、両方のビットが1の場合のみ対応するビットが1となり、それ以外の場合は対応するビットが0となります。
$ x=0755; y=0700; echo "obase=8; $((x&y))" | bc
700
$ x=0755; y=0644; echo "obase=8; $((x&y))" | bc
644
$ x=0xff; y=0x1a; printf "%x\n" $((x&y))
1a

下記のスクリプトはカレントディレクトリの全てのファイルに対して権限を確認する例です。
#!/usr/bin/bash

for file in `ls`
do
    if [[ -f $file ]]; then
        mod=($(stat -c "%a" ${file}))
        if [[ $((0$mod & 0600)) -eq 0600 ]]; then
            echo ${file}: 書き込み権、読み込み権あり
        elif [[ $((0$mod & 0600)) -eq 0400 ]]; then
            echo ${file}: 書き込み権あり
        elif [[ $((0$mod & 0600)) -eq 0200 ]]; then
            echo ${file}: 読み込み権あり
        else
            echo ${file}: アクセス権なし
        fi
    fi
done
12左から右^ビット単位の排他的OR演算子の左辺の値を右辺の値のビットと比較し、両方のビットが異る(一方のビットが1でもう一方のビットが0)場合のみ対応するビットが1となり、それ以外の場合は対応するビットが0となります。
$ x=$((2#00011010)); y=$((2#11111111)); echo "obase=2; $((x^y))" | bc;
11100101
13左から右|ビット単位のOR演算子の左辺の値を右辺の値のビットと比較し、どちらかまたは両方のビットが1の場合のみ対応するビットが1となり、両方のビットが0の場合は対応するビットが0となります。
$ x=$((2#00011010)); y=$((2#10010001)); echo "obase=2; $((x|y))" | bc;
10011011
14左から右&&論理的AND演算子の左辺を評価し、0以外であれば右辺を評価します。
両方の評価結果が0以外の場合のみ1を返し、それ以外の場合は0を返します。
左辺の評価結果が0であれば、右辺は評価されません。
$ x=3; y=5; z=6; echo $((x<y && y>z))
0
$ x=3; y=5; z=6; echo $((x<y && y<z))
1
$ x=3; y=5; z=6; echo $((x<y && 1/0))
bash: x<y && 1/0: 0 による除算です (エラーのあるトークンは "0")
$ x=3; y=5; z=6; echo $((x>y && 1/0))
0
最後の例は左辺の評価結果が0であるため、右辺が評価されず、0除算のエラーが発生しません。
15左から右||論理的OR演算子の左辺を評価し、0であれば右辺を評価します。
両方の評価結果が0の場合のみ0を返し、それ以外の場合は1を返します。
左辺の評価結果が0以外であれば、右辺は評価されません。
$ x=3; y=5; z=6; echo $((x<y || y>z))
1
$ x=3; y=5; z=6; echo $((x>y || y>z))
0
$ x=3; y=5; z=6; echo $((x>y || 1/0))
bash: x>y || 1/0: 0 による除算です (エラーのあるトークンは "0")
$ x=3; y=5; z=6; echo $((x<y || 1/0))
1
最後の例は左辺の評価結果が0以外であるため、右辺が評価されず、0除算のエラーが発生しません。
16右から左式1?式2:式3条件演算子式1の評価結果が0以外であれば、式2を評価してその結果を返します。
式1の評価結果が0であれば、式3を評価してその結果を返します。
$ x=23; y=5; echo $((x>20 ? x-10/y : x/y))
21
$ x=23; y=5; z=0; echo $((z!=0 ? x/z : x/y))
4
17右から左=代入演算子演算子の左辺の変数に右辺の値を代入し、その値を返します。
$ echo $((x=5)), $x
5, 5
*=代入演算子演算子の左辺の変数にその変数の値を右辺の値で乗算した値を代入し、その値を返します。
$ x=3; echo $((x*=4)), $x
12, 12
/=代入演算子演算子の左辺の変数にその変数の値を右辺の値で除算した値を代入し、その値を返します。
$ x=23; echo $((x/=3)), $x
7, 7
%=代入演算子演算子の左辺の変数にその変数の値を右辺の値で除算した剰余を代入し、その値を返します。
$ x=23; echo $((x%=3)), $x
2, 2
+=代入演算子演算子の左辺の変数にその変数の値に右辺の値を加算した値を代入し、その値を返します。
$ x=7; echo $((x+=3)), $x
10, 10
$ str=Hello; str+=" World"; echo $str
Hello World
$ animal=(dog cat); animal+=(bird pig); echo ${animal[3]};
pig
-=代入演算子演算子の左辺の変数にその変数の値から右辺の値を減算した値を代入し、その値を返します。
$ x=7; echo $((x-=3)), $x
4, 4
<<=代入演算子演算子の左辺の変数にその変数の値を右辺の値のビット数分だけ左にシフトした値を代入し、その値を返します。
$ for ((i=0,x=1; i<5; i++)); do echo $((x<<=1)); done
2
4
8
16
32
>>=代入演算子演算子の左辺の変数にその変数の値を右辺の値のビット数分だけ右にシフトした値を代入し、その値を返します。
負の値を右ビットシフトした場合、符号拡張が行われます。
$ for ((i=0,x=1024; i<5; i++)); do echo $((x>>=2)); done
256
64
16
4
1
&=代入演算子演算子の左辺の変数にその変数の値を右辺の値のビットと比較し、両方のビットが1の場合のみ対応するビットが1となり、それ以外の場合は対応するビットが0となる値を代入し、その値を返します。
$ x=0755; y=0700; ((x&=y)); echo "obase=8; $x" | bc
700
$ x=0755; y=0644; ((x&=y)); echo "obase=8; $x" | bc
644
$ x=0xff; y=0x1a; ((x&=y)); echo "obase=16; $x"| bc
1A
^=代入演算子演算子の左辺の変数にその変数の値を右辺の値のビットと比較し、両方のビットが異る(一方のビットが1でもう一方のビットが0)場合のみ対応するビットが1となり、それ以外の場合は対応するビットが0となる値を代入し、その値を返します。
$ x=$((2#00011010)); y=$((2#11111111)); ((x^=y)); echo "obase=2; $x" | bc
11100101
|=代入演算子演算子の左辺の変数にその変数の値を右辺の値のビットと比較し、どちらかまたは両方のビットが1の場合のみ対応するビットが1となり、両方のビットが0の場合は対応するビットが0となる値を代入し、その値を返します。
$ x=$((2#00011010)); y=$((2#10010001)); ((x|=y)); echo "obase=2; $x" | bc
10011011
18左から右式1,式2カンマ演算子演算子の左辺と右辺の両方を評価し、右辺の評価結果の値を返します。
$ echo $((x=5, y=x*2))
10
$ echo $((x=5, y=x*2, z=y+x))
15

演算子は優先度の高いものから順に評価され、同じ優先度の演算子が1つの式の中に複数現れた場合、結合規則に従って評価されます。
同じ優先度の演算子の式を()で囲むことによって、優先的に評価することができます。


条件式

シェルでは複合コマンド[[組み込みコマンドtestと[を組み合わせることによって条件式が評価されます。

bashの条件式は演算子と単項または二項のプライマリで構成され、真(0)または偽(1)を返します。
[[]]の間にある単語に対してはパス名展開単語の分割は行われません。チルダ展開パラメータ展開算術式展開コマンド置換、プロセス置換、クォートの削除は行われます。
条件式の演算子はクォートされてはいけません。
プライマリはファイル、シェルオプション名、変数名、文字列、整数のいずれかです。
プライマリがファイルの場合は以下の通りとなります。

ファイルの指定説明
ファイル名またはパスシンボリックリンクが指定された場合、リンク先のファイルが対象となります。
それ以外の場合、指定されたファイルまたはパスが対象となります。
/dev/fd/nの形式ファイルディスクリプタnが対象となります。
/dev/stdinの形式標準入力が対象となります。
/dev/stdoutの形式標準出力が対象となります。
/dev/stderrの形式標準エラー出力が対象となります。

bashの条件式で使用できる演算子とプライマリの組み合わせは以下の通りとなります。
演算子とプライマリの指定説明
-a ファイルファイルが存在していれば真、存在していなければ偽を返します。
-b ファイルファイルが存在し、かつブロック特殊ファイルであれば真、それ以外であれば偽を返します。
-c ファイルファイルが存在し、かつキャラクター特殊ファイルであれば真、それ以外であれば偽を返します。
-d ファイルファイルが存在し、かつディレクトリであれば真、それ以外であれば偽を返します。
-e ファイルファイルが存在していれば真、存在していなけれ ば偽を返します。
-f ファイルファイルが存在し、かつ通常ファイルであれば真、それ以外であれば偽を返します。
-g ファイルファイルが存在し、かつSGIDが設定されていれば真、それ以外であれば偽を返します。
-h ファイルファイルが存在し、かつシンボリックリンクであれば真、それ以外であれば偽を返します。
-k ファイルファイルが存在し、かつスティッキービットが設定されていれば真、それ以外であれば偽を返します。
-p ファイルファイルが存在し、かつ名前付きパイプ(FIFO)であれば真、それ以外であれば偽を返します。
-r ファイルファイルが存在し、かつ読み込み可能(読み込み権限がある)であれば真、それ以外であれば偽を返します。
-s ファイルファイルが存在し、かつそのファイルのサイズが0より大きければ真、それ以外であれば偽を返します。
-t ファイルディスクリプタファイルディスクリプタがオープンされており、かつ端末を参照(標準入出力など)していれば真、それ以外であれば偽を返します。
-u ファイルファイルが存在し、かつSUIDが設定されていれば真、それ以外であれば偽を返します。
-w ファイルファイルが存在し、かつ書き込み可能(書き込み権限がある)であれば真、それ以外であれば偽を返します。
-x ファイルファイルが存在し、かつ実行可能(実行権限がある)であれば真、それ以外であれば偽を返します。
-G ファイルファイルが存在し、かつ現在のシェルの実効グループIDに所有されていれば真、それ以外であれば偽を返します。
-L ファイルファイルが存在し、かつかつシンボリックリンクであれば真、それ以外であれば偽を返します。
-N ファイルファイルが存在し、最終アクセス日時以降にファイルの内容が変更されていれば真、それ以外であれば偽を返します。
-O ファイルファイルが存在し、かつ現在のシェルの実効ユーザーIDに所有されていれば真、それ以外であれば偽を返します。
-S ファイルファイルが存在し、かつソケットであれば真、それ以外であれば偽を返します。
ファイル1 -ef ファイル2ファイル1とファイル2のデバイス番号とiノード番号が同じであれば真、それ以外であれば偽を返します。
ファイル1 -nt ファイル2ファイル1とファイル2の最終修正日時を比較し、ファイル1がより新しいか、ファイル1が存在していてファイル2が存在しなければ真、それ以外であれば偽を返します。
ファイル1 -ot ファイル2ファイル1とファイル2の最終修正日時を比較し、ファイル2がより新しいか、ファイル2が存在していてファイル1が存在しなければ真、それ以外であれば偽を返します。
-o オプションシェルのオプションが有効であれば真、それ以外であれば偽を返します。
setコマンドの-oオプションで指定できるオプションが指定できます。
-v 変数変数が設定されて(値が代入されて)いれば真、それ以外であれば偽を返します。
-z 文字列文字列の長さが0であれば真、それ以外であれば偽を返します。
-n 文字列文字列の長さが0でなければ真、それ以外であれば偽を返します。
文字列1 == 文字列2
または
文字列1 = 文字列2
文字列1と文字列2が同じであれば真、それ以外であれば偽を返します。
POSIXに準拠する場合、==ではなく、=を使用する必要があります。
文字列2はパターンとして解釈されパターンマッチングで評価されます。
パターンの一部または全体をクォートすることで単純な文字列としてマッチングさせることができます。
シェルのオプションnocasematchが有効であればパターンマッチングでアルファベットの大文字と小文字の区別はされません。

実行例:
$ WORD="Hello World"
$ [[ ${WORD} == Hello* ]] && echo True || echo False
True
*は任意の文字列にマッチするため条件式は真となります。

$ WORD="Hello World"
$ [[ ${WORD} == "Hello*" ]] && echo True || echo False
False
パターンがダブルクォートでクォートされているため*が単純な文字として認識されて一致しないため条件式は偽となります。

$ WORD="Hello World"
$ [[ ${WORD} == Hello * ]] && echo True || echo False
bash: 条件式に構文エラーがあります
bash: `*' 周辺に構文エラーがあります
文字列2に空白文字が含まれているため、2つの単語として認識され構文エラーとなります。

$ WORD="Hello World"
$ [[ ${WORD} == Hello\ * ]] && echo True || echo False
True
上記例のエラーを回避するために空白文字をバックスラッシュでクォートしています。これで空白文字は単純な文字として認識され、続く*は任意の文字列にマッチするため条件式は真となります。

$ WORD="Hello World"
$ WORD2="Hello *"
$ [[ ${WORD} == ${WORD2} ]] && echo True || echo False
True
$で始まる${WORD2}はパラメータ展開されます。${WORD2}の値に含まれる*は任意の文字列にマッチするため条件式は真となります。

$ WORD="Hello World"
$ WORD2="Hello *"
$ [[ "${WORD}" == "${WORD2}" ]] && echo True || echo False
False
$で始まる${WORD}、および${WORD2}はパラメータ展開されます。どちらもダブルクォートでクォートされているため${WORD2}の値に含まれる*は単純な文字として認識されて一致しないため条件式は偽となります。

$ WORD="Hello World"
$ [[ ${WORD} == [[:alpha:]]* ]] && echo True || echo False
True
$ [[ ${WORD} == Hello[[.space.]]* ]] && echo True || echo False
True
パターンには文字クラス(Character Class)等値クラス(Equivalence Class)照合シンボル(Collating Symbol)文字クラスや照合シンボルを組み合わせることも可能です。

$ WORD="Hello World"
$ [[ [[:alpha:]]* == ${WORD} ]] && echo True || echo False
False
文字列1はパターンとして解釈されず、単純な文字列として扱われ一致しないため条件式は偽となります。

$ shopt -s nocasematch
$ WORD="Hello World"
$ [[ ${WORD} == hello* ]] && echo True || echo False
True
シェルのオプションnocasematchが有効であり英大文字と英小文字は同じとして扱われて文字列にマッチするため条件式は真となります。
文字列1 != 文字列2文字列1と文字列2が同じでなければ真、それ以外であれば偽を返します。
文字列2はパターンとして解釈されパターンマッチングで評価されます。
パターンの一部または全体をクォートすることで単純な文字列として マッチングさせることができます。
シェルのオプションnocasematchが有効であればパターンマッチングで アルファベットの大文字と小文字の区別はされません。
文字列1 =~ 文字列2文字列2を拡張正規表現と見做して文字列1と文字列2を比較します。
パターンがマッチすれば真、それ以外であれば偽を返します。正規表現が文法的に誤っている場合、2を返します。
パターンの一部または全体をクォートすることで単純な文字列として マッチングさせることができます。
シェルのオプションnocasematchが有効であればマッチングでアルファベットの大文字と小文字の区別はされません。
正規表現中の括弧による部分式にマッチした部分文字列は配列変数"BASH_REMATCHに保存されあとから参照可能となります。BASH_REMATCHのキーが0の要素は正規表現全体にマッチした文字列が保存され、キーがnの要素は正規表現のn番目の括弧の部分式にマッチした文字列が保存されます。

実行例:
$ DATETIME=$(date "+%Y/%m/%d %H:%M:%S"); echo ${DATETIME}
2022/03/13 18:09:35
$ [[ ${DATETIME} =~ ^([0-9]{4})\/([0-9]{2})\/([0-9]{2})\ ([0-9]{2}):([0-9]{2}):([0-9]{2})$ ]]
$ echo ${BASH_REMATCH[0]}
2022/03/13 18:09:35
$ echo ${BASH_REMATCH[1]}
2022
$ echo ${BASH_REMATCH[2]}
03
$ echo ${BASH_REMATCH[3]}
13
$ echo ${BASH_REMATCH[4]}
18
$ echo ${BASH_REMATCH[5]}
09
$ echo ${BASH_REMATCH[6]}
35
文字列1 < 文字列2現在のロケールで文字列1と文字列2を比較した場合、文字列1が文字列2よりも辞書順で前にあれば真、それ以外であれば偽を返します。
文字列1 > 文字列2現在のロケールで文字列1と文字列2を比較した場合、文字列1が文字列2よりも辞書順で後にあれば真、それ以外であれば偽を返します。
整数1 -eq 整数2整数1と整数2が等しければ真、それ以外であれば偽を返します。
整数1 -ne 整数2整数1と整数2が等しくなければ真、それ以外であれば偽を返します。
整数1 -lt 整数2整数1が整数2よりも小さければ真、それ以外であれば偽を返します。
整数1 -le 整数2整数1が整数2よりも小さいか等しければ真、それ以外であれば偽を返します。
整数1 -gt 整数2整数1が整数2よりも大きければ真、それ以外であれば偽を返します。
整数1 -ge 整数2整数1が整数2よりも大きいか等しければ真、それ以外であれば偽を返します。