bashメモ。

# メモ

Ubuntu 16.04

最近Bashを少しかじりました(数日程度)。
もう使うことは無いと思いますが、また機会があったときのためのメモです。

多少コマンドが打てること、他のプログラミング言語が扱えることを前提としてます。

シバンの設定は、ここではほとんど省略してます。



https://qiita.com/diconran/items/cee8dd6bbd553ada1078

ついでに、ある程度複雑なコードになったらVisual Studio Code使ったほうが便利。



## 基本


viでもGEditでもなんでもいいのでとりあえずファイルを作成

    vi sample

こんな感じで記述して保存します。

``` bash
#!/bin/bash

echo Hello
```

保存したら実行権限を与えます。

    chmod +x sample

実行する

    ./sample


結果がこちら

    Hello


一行目の「#!/bin/bash」はシバンと呼ばれるもので,インタプリタへのパスです。
この例では/bin/bashを指定してます。ノーティラスで/bin/bashを確認してください。

**インタプリタはスクリプト言語で書かれたコードを実行するためのプログラム**

echoで文字列を表示。
シェルスクリプトではダブルクォートで括る必要がない場合もあります
    

### 文字列の扱いとコメント

```bash
#!/bin/bash

# ここはコメントです。

echo http://127.0.0.1:80/view?id=3#ccc

echo 10+20-5

echo "Hello World"

echo 'Hello World'

echo "AB\"CD\"EFG" # "の中に"を入れるには\"と書く

echo 'let'"'"'s go'

echo "AB""CD""EFG"

echo 'AB''CD''EFG'
```

```txt
http://127.0.0.1:80/view?id=3#ccc
10+20-5
Hello World
Hello World
AB"CD"EFG
let's go
ABCDEFG
ABCDEFG
```

まずは、

    # ここはコメントです。

「#」の後に書かれる文字列はコメントになり、無視されます。
他の言語では「//」に当たるものです。
ただし、１行目にあるのはシバンですので別の意味を持ちます。

bashの恐ろしいというか、奇妙なところは、以下の例(記号含む)も一応表示されてしまう点です。

    echo http://127.0.0.1:80/view?id=3#ccc

一見演算子に見えても

    echo 10+20-5

他言語の感覚では「え！？」って感じですよね。
数値や+や-だって文字列で表示してしまいます。


ただ、

    ; < > | & ( )

などはそのまま使えません。「#」や「$」なども場所によっては問題になります。
他にもありますが面倒なので省きます。後述するエスケープで解説します。


    echo "AB\"CD\"EFG"

ダブルクォート中にダブルクォートを埋め込みたいときは、バックスラッシュを使います。環環境によっては円記号のやつです。
エスケープといいます。



ただシングルクォート中はエスケープ出来ないようで、

    echo 'let\'s go'

と書くとエラーになります。代わりに

    echo 'let'"'"'s go'

と書きます。
見づらいので空白を入れてみましょう。

    'let' "'" 's go'

文字列は連結出来ます。

    echo "AB""CD""EFG"
    echo 'AB''CD''EFG'





### エスケープ

```bash
echo ABC\(\)\|\<\>\&\$DEF\\G
echo "ABC\(\)\|\<\>\&\$DEF\\G"
echo 'ABC\(\)\|\<\>\&\$DEF\\G'
```

```txt
ABC()|<>&$DEF\G
ABC\(\)\|\<\>\&$DEF\G
ABC\(\)\|\<\>\&\$DEF\\G
```

クォートで囲まれた場合とそうでない場合でだいぶ結果が違います。
囲んだ場合はバックスラッシュがほとんどそのまま表示されてます。

ダブルクォートとシングルクォートで「$」に違いが出てるのは次で説明します。


**以降、シバンは省略します。**




### 変数

```bash
str="Hello World"
echo $str
```

<pre>
Hello World
</pre>


str変数に文字列を代入してます。
変数を利用するときは先頭にドル記号を記述します。

**注意しないとけいないのは、イコールの両脇に空白を入れるとエラーになる点です。**
他言語利用者だとよく引っかかる箇所ですね。


### 変数を文字列に埋め込む。

```bash
str="Hello World"
echo ${str}

name="Yamada"
echo "Hi ${name}!"
```

<pre>
Hello World
Hi Yamada!
</pre>

文字列に代入する際は　**${str}**　などとします。
変数名に他の文字列がくっつかなければ$strでもいいです。





### 変数のエスケープと展開

```bash
str="Hello World"
echo "wq = ${str}"
echo 'wp = ${str}'
echo "esc = \${str}"
```

```txt
wq = Hello World
wp = ${str}
esc = ${str}
```

シングルクォート中では変数は展開されません。
ダブルクォート中でバックスラッシュにより「$」はエスケープされます。


### 変数の注意点


```bash
a=3
b=5
nbr=${a}+${b}

echo "nbr = ${nbr}"
```

<pre>
nbr = 3+5
</pre>

前回でも紹介しましたが、+や-はそのままでは他言語のように演算子として機能しません。




### 単純な計算する


```bash
a=3
b=5

nbr=`expr $a + $b`
echo "nbr = ${nbr}"

nbr2=$(( $a + $b ))
echo "nbr2 = ${nbr2}"
```

<pre>
nbr = 8
nbr2 = 8
</pre>

計算はexprや$(())などが使えます。
$(())のほうが高機能。


例えば次のコマンドを打った場合、5が表示される。

    $ expr 3 + 2

表示ではなく値を取得したい場合は今回のようにバッククォートを使います。
バッククォートで囲んで、中身の式を実行して結果を取得してます。







### ヒアドキュメント

「ここからここまで文字列ですー！」というような範囲。
Pがつくスクリプト系言語にはおなじみのやつです。

```bash
name="Yamada"

cat << DOC
<h1>Wellcom!!</h1>
Hi. ${name}!
DOC
```

```txt
<h1>Wellcom!!</h1>
Hi. Yamada!
```

DOCの部分は何でもいいです。
最後のDOCの行には空白など入れてはいけません。警告が出ます。

### ヒアドキュメントで変数を展開したくないとき

変数を展開したくない時はDOCをクォートでくくります。

```bash
name="Yamada"

cat << 'DOC'
<h1>Wellcom!!</h1>
Hi. ${name}!
DOC
```

```txt
<h1>Wellcom!!</h1>
Hi. ${name}!
```


### ヒアドキュメントから文字列として取得したいときは

```bash
name="Yamada"

txt=$(cat <<DOC
<h1>Wellcom!!</h1>
Hi. ${name}!
DOC
)

echo "${txt}"
```






## 条件分岐

こちらが参考になります。

http://shellscript.sunone.me/if_and_test.html




### 文字列の比較、条件に一致した場合。

```bash
str1="ABC"
str2="ABC"

# 左右の文字列がが等しい
if [ "$str1" = "$str2" ]; then
    echo "str1 = str2" # 実行される
fi

# 左右の文字列が等しくない
if [ "$str1" != "$str2" ]; then
    echo "str1 != str2" # 実行されない
fi
```

    str1 = str2

変数にはダブルクォートをつける習慣をつけていたほうが無難です。
今回の例ではつける必要ありませんが。
理由はこのページの最後らへんに書いてます。


### 数値の比較

```bash
age=28

# 20未満であればNG
if [ "$age" -lt 20 ]; then
    echo "しっしっ！"
else
    echo "いらっしゃいませ！"
fi
```

    いらっしゃいませ！


### 数値の比較に(())を使う

```bash
age=28

if ((age < 20)); then
    echo "しっしっ！"
else
    echo "いらっしゃいませ！"
fi
```

    いらっしゃいませ！


後で[]とtest、(())とletについて少し触れます。



## ループ

```bash
for i in {1..10}; do
    echo "i = ${i}"
done
```

```txt
i = 1
i = 2
i = 3
i = 4
i = 5
i = 6
i = 7
i = 8
i = 9
i = 10
```

1から10まで繰り返します。


### ループ：seq

```bash
min=1
max=10

for i in $(seq $min $max); do
    echo "i = ${i}"
done
```

結果は前回と同じです。
seqを使うと変数で範囲を指定出来ます。


### ループ：値のリスト

```bash
for p in 2 3 5 7 11 13 17; do
    echo "prime = ${p}"
done
```

```txt
prime = 2
prime = 3
prime = 5
prime = 7
prime = 11
prime = 13
prime = 17
```


### ループ：コマンド

```bash
for f in $(ls); do
    echo "file = ${f}"
done
```

lsを使って一覧を表示します。
結果は書きません。


## 配列

### 配列いろいろ

```bash
arr=(Apple Banana Orange Strawberry) # 配列の初期化。ついカンマつけないよう注意
arr[10]=Grape # indexが10の要素の設定

echo "arr.length = ${#arr[@]}" # 要素数
echo "arr[1] = ${arr[1]}" # indexが1の要素：0から数える
echo "arr[10]" = ${arr[10]} # indexが10の要素
echo "arr = ${arr}" # 先頭の要素
echo "arr = ${arr[@]}" # まとめて表示：＠ではなく*でも可

echo ${arr[@]:1:2} # indexが1から2個


arr2=(1 2 3 4 5 6 7 8 9)
echo ${arr2[@]:2:4} #indexが2から4個
```

```txt
arr.length = 5
arr[1] = Banana
arr[10] = Grape
arr = Apple
arr = Apple Banana Orange Strawberry Grape
Banana Orange
3 4 5 6
```

    arr=(Apple Banana Orange Strawberry)

()で配列を初期化してます。
他言語のようについカンマをつけてしまいがちですので注意してください。


    arr[10]=Grape
    ${#arr[@]}

同じことをJavaScriptでやるとlengthで11を取得しますよね、だから何という例ですが。

### 配列とループ

```bash
arr=(Apple Banana Lemon Grap)

for elm in ${arr[@]}; do
    echo $elm
done
```

```txt
Apple
Banana
Lemon
Grap
```



### 配列に空白が含まれるときのループの注意点


```bash
arr=("A B" "C D" "E F G")

for s in ${arr[@]}; do
  echo "STR = ${s}"
done

for s in "${arr[@]}"; do
  echo "STR2 = ${s}"
done
```


```bash
STR = A
STR = B
STR = C
STR = D
STR = E
STR = F
STR = G
STR2 = A B
STR2 = C D
STR2 = E F G
```

    for s in ${arr[@]}; do

↑これをやってしまうと、要素に空白が含まれていると、その文字列が空白で分割されてしまいます。


    for s in "${arr[@]}"; do

↑それが不都合な場合はダブルクォートでくくります。

こちらがとても参考になります。

http://www.task-notes.com/entry/20150119/1421646435


### 連想配列

```bash
declare -A hmn

hmn["name"]=Yamada
hmn["age"]=30

echo "Name = ${hmn[name]}"
echo "Age = ${hmn[age]}"
```

<pre>
Name = Yamada
Age = 30
</pre>

**比較的最近の機能らしいので注意してください**



### 二次元配列・多次元配列もどき

無い、らしいです。
ただ最近は連想配列があるのでそれを使えば楽にできますね。

連想配列が使えない場合は別の方法を用意てそれっぽいことをします。
evalを利用し、変数を動的に操作することでそれっぽいことができるようです。

http://www.kurobuti.com/blog/?p=4880





## 関数

コマンドなどをまとめておいて後で一気に呼び出すような機能！？

```bash
function showMessage()
{
    echo "Hello World"
}

showMessage
```

    Hello World

関数名(showMessage)の名前は自由につけてください。

    showMessage

関数を呼び出すと、関数の中身が実行されます。
他言語と違って呼び出す際は()は入りません。
またfunctionは省略出来ます。


```bash
showMessage()
{
    echo "Hello World"
}

showMessage
```

### 引数

関数にデータを渡すことが出来ます。

```bash
function showMessage()
{
    echo "0 = ${0}"
    echo "1 = ${1}"
    echo "2 = ${2}"
    echo "3 = ${3}"
    echo "length = ${#}"
    echo "params = ${@}"
}

showMessage one two three
```

```txt
0 = /home/dicon/dev/vsc/qiita/sample.sh
1 = one
2 = two
3 = three
length = 3
params = one two three
```

関数内では\$1, \$2 \$3 という名前で引数を取得出来ます。
1から数えます。\$0は見ての通り実行元のシェルのファイル名を取得します。

$@は引数をまとめて表示します。
ただ配列のループはうっかりやりがちのミスにご注意。

```bash
function showMessage()
{
    for item in $@; do
        echo "Item = ${item}"
    done

    for item in "${@}"; do
        echo "Item = ${item}"
    done
}

showMessage "zero one" two three
```

```txt
Item = zero
Item = one
Item = two
Item = three
Item = zero one
Item = two
Item = three
```


### 関数の戻り値

bashは関数に戻り値は無いようです。

```bash
function add()
{
    echo $(($1 + $2))
}

add 3 5
add 101 10
```

```txt
8
111
```


戻り値っぽいことする場合は$()か``を使います。


```bash
function add()
{
    echo $(($1 + $2))
}

v1=$(add 3 5)
v2=$(add 101 10)

echo "V1 = ${v1}, V2 = ${v2}"
```

    V1 = 8, V2 = 111


### returnというキーワード

一応bashにもreturnがあります。ただし、他言語とは意味が違います。

```bash
function f()
{
    echo "Hello World"
    return 0
}

v=$(f)
echo "V = ${v}"
echo "return = ${?}"
```

```txt
V = Hello World
return = 0
```


returnは「終了ステータス」というものらしいです。
その値は$?で取得出来ます。

返せる値は0〜255までの符号なし8ビット整数の範囲で、範囲外はANDによって調整されるようです。

http://bokko.hatenablog.com/entry/20090223/1235400626


0を返せば成功の意味。それ以外(1とか)はエラーの意味。



## 最後に、復習兼ねて。

### バッククォートと$()

```bash
p=`pwd`
u=`whoami`
d=`ls -al | grep hell`

echo "pwd = ${p}"
echo "user = ${u}"
echo "------ dir ------"
echo "$d"
```

```bash
pwd = /home/dicon
user = dicon
------ dir ------
-rw-r--r--  1 dicon dicon 12288  9月 29 18:59 .exshell.swp
-rwxr-xr-x  1 dicon dicon    64 10月  3 01:19 cchell
-rwxr-xr-x  1 dicon dicon  6853 10月  4 19:27 drawshell
-rwxr-xr-x  1 dicon dicon  3894 10月  1 01:34 exshell
```

バッククォートで囲むとコマンドを実行して、その結果を取得します。
これと同じことが$()でも出来ます。


```bash
p=$(pwd)
u=$(whoami)
d=$(ls -al | grep hell)

echo "pwd = ${p}"
echo "user = ${u}"
echo "------ dir ------"
echo "$d"
```

結果はさっきと同じです。



### expr

    $ expr 1 + 2 + 3
    > 6
    $ expr 1 \* 2 \* 3
    > 6
    $ expr \( 2 + 3 \) \* 5
    > 25



exprコマンドで簡単な計算をします。 * や　() などはエスケープします。
また、数字間、記号間は空けないと構文エラーになったり予想外な結果をだしたりします。

    $ expr 1+2+3
    > 1+2+3

↑などとするとそのまま「1+2+3」が出力されました

    $ expr 1+2 +3
    > 構文エラー

↑では構文エラーになりました。

---

結果を取得するには次のようにします。

    $ echo `expr 1 + 2 + 3`
    $ echo $(expr 1 + 2 + 3)






### letと(())と$(())

letを使って計算が出来ます。インクリメントも出来ます。

```bash
let a=3+5-2
echo $a

b=110
let b++
echo "b = ${b}"


let a=2+5-3 b=4*5 c=10/5
echo "a = ${a} b = ${b} c = ${c}"
```

```txt
6
b = 111
a = 4 b = 20 c = 2
```


---

(())を使った計算のほうが使いやすい


```bash
((a=3+5-2))
echo $a

b=110
((b++))
echo "b = ${b}"

((a=2+5-3, b=4*5, c=10/5))
echo "a = ${a} b = ${b} c = ${c}"
```

```txt
6
b = 111
a = 4 b = 20 c = 2
```

---

ところで、exprと違ってletで注意する点は、

    $ let aaa=1+2+3; echo $aaa
    > 6

↑一列につながってればOK

    $ let aaa=1 + 2 + 3; echo $aaa
    > 構文エラー

↑つながってないとNG


    $ let aaa="1 + 2 + 3"; echo $aaa
    > 6

↑クォートでくくればOK

一方、(())の方はその必要なし。

    $ ((aaa = 1 + 2 + 3)); echo $aaa
    > 6

こちらのほうが使いやすいですね。

---


$(())で計算結果を取得出来ます。

```bash
((a = 1 + 2 + 3 + 4))
echo "a = ${a}"

b=$(( 1 + 2 + 3 + 4 ))
echo "b = ${b}"
```

```txt
a = 10
b = 10
```




### bcを使う・浮動小数点も使える

```bash
echo $((5 / 2))
echo $((10 / 3))

echo "-- bc --"
echo "scale=1; 5 / 2" | bc
echo "scale=5; 10 / 3" | bc # 第３まで表示
echo "1 + 2 + 3" | bc
echo "2^8" | bc # 2の8乗
```

```txt
2
3
-- bc --
2.5
3.33333
6
256
```

letやexprでは浮動小数点数を扱えません。
bcを使うとそれも可能になります。

計算結果を取得するには以下のようにします。

```bash
nbr=$(echo "scale=1; 5 / 2" | bc)
echo "nbr = ${nbr}"
```

    2.5

exprよりletのほうが高機能で高速、
bcは浮動小数点数使えて更に高機能


## その他、メモ。

### ls -alからファイル一覧を表示する。


```bash
arr=$(ls -al)
for item in "${arr[@]}"; do
    echo "Item = ${item}\n"
done
```

```bash
IFS_BU=$IFS
IFS=$'\n'

for f in $(ls -al); do
    echo "File = ${f}"
done

IFS=$IFS_BU
```






## bashにおける変数の罠

ここからは未解決＆不正確かもしれない内容を含みます(調べるのめんどうで匙投げたため)

---

未定義を含む引数におけるダブルクォートの有無

```bash
function func()
{
    echo "length = ${#}"
    echo "1 = ${1}"
    echo "2 = ${2}"
    echo "3 = ${3}"
    echo "4 = ${4}"
}

unset param1
param2="2"
unset param3
param4="4"

func $param1 $param2 $param3 $param4

echo "==================="

func "$param1" "$param2" "$param3" "$param4"
```

```txt
length = 2
1 = 2
2 = 4
3 = 
4 = 
===================
length = 4
1 = 
2 = 2
3 = 
4 = 4
```

まずunsetについてですが、明示的に変数を未定義にしてます。
param1やparam3は定義されていないものとしてます。

未定義の変数の存在は無視されてますね。２つしか渡されてません。
でも二回目の呼び出しでは引数が４つあります。

---

今度はこちらの動作も注意する必要があります。


```bash
function func()
{
    echo "length = ${#}"
    echo "1 = ${1}"
    echo "2 = ${2}"
    echo "3 = ${3}"
    echo "4 = ${4}"
}

param1="zero one"
param2=""
param3="three"
param4=""

func $param1 $param2 $param3 $param4

echo "==================="

func "$param1" "$param2" "$param3" "$param4"
```

```txt
length = 3
1 = zero
2 = one
3 = three
4 = 
===================
length = 4
1 = zero one
2 = 
3 = three
4 = 
```
"zero one"がzeroとoneの２つの引数にわけられていることがわかります。
理由は以下のif例なのかなーと思ってます。

---

ifの比較で$strがダブルクォート無しだと

```bash
str="apple banana"

if [ "apple banana" = $str ]; then
    echo "OK"
else
    echo "NG"
fi

```

    /home/dicon/dev/vsc/qiita/sample.sh: 5 行: [: 引数が多すぎます
    NG

警告出ます。結果はNGが表示されました。
＊実際にはシバンを省略しているので5行目ではありません。

ダブルクォートなしでエラーになるのはなんでだろう？


```bash
str="apple banana"

if [ "apple banana" = apple banana ]; then
    echo "OK"
else
    echo "NG"
fi
```

    /home/dicon/dev/vsc/qiita/sample.sh: 5 行: [: 引数が多すぎます
    NG

エラーは同じだし、こういうことでしょうか？
先に空白入りの変数が展開されてしまうために起こるのかなーと(自身はないです)


---


$strがダブルクォート有りだと

```bash
str="apple banana"

if [ "apple banana" = "$str" ]; then
    echo "OK"
else
    echo "NG"
fi
```

    OK

こちらは成功します。


[[]]を使うとダブルクォートする必要はない。

```bash
str="apple banana"

if [[ "apple banana" = $str ]]; then
    echo "OK"
else
    echo "NG"
fi
```

    OK


意図通りの結果になります。
空文字の場合も見ておきましょう。

```bash
title=""

if [ $title = "" ]; then
    echo "タイトルが空です"
fi
```

    /home/dicon/dev/vsc/qiita/sample.sh: 5 行: [: =: 単項演算子が予期されます

```bash
title=""

if [ = "" ]; then
    echo "タイトルが空です"
fi
```

上と同じエラーになりました。

    if [ "$title" = "" ];
    if [[ $title = "" ]]

としましょう。よく引っかかりますので注意しましょう。




### testと[]

```bash
if [ "abc" = "abc" ]; then
    echo "TRUE"
fi
```

    TRUE


[]の正体はtest

```bash
if test "abc" = "abc"; then
    echo "TRUE"
fi
```

    TRUE


testは式の評価を終了ステータスで取得可能

    $ test "abc" = "abc"; echo $?
    > 0

成功すると0

    $ test "abc" = "abcd"; echo $?
    > 1

失敗すると1(0以外)






## ||と&&と!

プログラミングでは欠かせない、論理演算子とかを。

```bash

# No.1
if [ "a" = "a" ] && [ "b" = "b" ]; then
    echo "No.1 TRUE"
fi

# No.2
if [ "a" = "a" ] && [ "b" = "B" ]; then
    echo "No.2 TRUE"
fi

# No.3
if [ "a" = "a" ] || [ "a" = "B" ]; then
    echo "No.3 TRUE"
fi
```


```txt
No.1 TRUE
No.3 TRUE
```

&&は左右の結果がともに真(bashの場合は終了ステータス0が真)であれば&&の評価は真となります。

||は左右の結果のどちらかが真であれば||の評価が真になります。

更に詳しく見ていきましょう。

```bash
[ "a" = "a" ] && [ "b" = "b" ]; echo $?
[ "a" = "A" ] && [ "b" = "b" ]; echo $?
[ "a" = "a" ] && [ "b" = "B" ]; echo $?
[ "a" = "A" ] && [ "b" = "B" ]; echo $?

echo "========================="

[ "a" = "a" ] || [ "b" = "b" ]; echo $?
[ "a" = "A" ] || [ "b" = "b" ]; echo $?
[ "a" = "a" ] || [ "b" = "B" ]; echo $?
[ "a" = "A" ] || [ "b" = "B" ]; echo $?
```

```txt
0
1
1
1
=========================
0
0
0
1
```
大文字で書いているところが偽になるようにしてます。
&&は左右が共に真の場合だけ真。
||は左右が共に偽の場合だけ偽。



## 冒険をしてみよう。

---

**ここからは実験的なものになります。
ここに書いているコードが正しいのかどうか知りません。
調べてません。あくまで遊びで書いてるコードです。**

---


[]はtest、ということは、ifは終了ステータスで分岐してるの？
実験してみよう


https://robots.thoughtbot.com/the-unix-shells-humble-if

ifの評価には関数も行けそうだ。


```bash
function trueFunc()
{
    return 0
}

function falseFunc()
{
    return 1
}

if trueFunc; then
    echo "trueFunc = TRUE"
else
    echo "trueFunc = FALSE"
fi

if falseFunc; then
    echo "falseFunc = TRUE"
else
    echo "falseFunc = FALSE"
fi
```

```txt
trueFunc = TRUE
falseFunc = FALSE
```
終了ステータスを論理値として使えそうな。





次は&&と||の評価のされかたです。

&&は、左の結果が偽であれば右の評価はされません(左が偽だった時点で右の評価をしなくても偽は決定しているため)

||は、左の結果が真であれば右の評価はされません(左が真だった時点で右は評価しなくても真が決定してるため)

その様子を見ていきましょう。


```bash
function trueFunc()
{
    echo "TRUE-FUNC"
    return 0
}

function falseFunc()
{
    echo "FALSE-FUNC"
    return 1
}

function putLine()
{
    echo "++++++++++++++++++++"
}

trueFunc && trueFunc; putLine
trueFunc && falseFunc; putLine
falseFunc && trueFunc; putLine
falseFunc && falseFunc; putLine

echo "//////////////////////////////"
echo "//////////////////////////////"

trueFunc || trueFunc; putLine
trueFunc || falseFunc; putLine
falseFunc || trueFunc; putLine
falseFunc || falseFunc; putLine
```


```txt
TRUE-FUNC
TRUE-FUNC
++++++++++++++++++++
TRUE-FUNC
FALSE-FUNC
++++++++++++++++++++
FALSE-FUNC
++++++++++++++++++++
FALSE-FUNC
++++++++++++++++++++
//////////////////////////////
//////////////////////////////
TRUE-FUNC
++++++++++++++++++++
TRUE-FUNC
++++++++++++++++++++
FALSE-FUNC
TRUE-FUNC
++++++++++++++++++++
FALSE-FUNC
FALSE-FUNC
++++++++++++++++++++
```

&&の場合は、左が偽の時点でそれ以降評価されてません。
||の場合は、左が真の時点でそれ以降評価されてません。
このことが確認できたと思います。


スクリプト系の言語ではこの性質を利用して

```bash
[ "a" = "a" ] && echo "if"
[ "a" = "B" ] && echo "else"

echo "==============="

[ "a" = "a" ] || echo "if"
[ "a" = "B" ] || echo "else"
```

```txt
if
===============
else
```

こんな利用をすることがあります。
具体的には、

```bash
unset title

[ -n "$title" ] || title="無題"

echo $title
```

    無題

-nは"$title"が1文字以上あれば真となるオプション。
つまり、文字列が1文字以上なければ、右の式が実行さえる。
bashだとifって結構面倒ですよね。式を短く書くときに利用してます。
これ、いいのかどうかはわかりません。

最後にビックリマーク。

```bash
[ "a" = "A" ]; echo $?
! [ "a" = "A" ]; echo $?
```

    1
    0

!は真を偽に、偽を真に、つまり反転させます。


### letの真偽

```bash
if (( 3 < 5 )); then
    echo " 3 < 5 == TRUE"
fi

if (( 3 > 5 )); then
    echo " 3 > 5 == TRUE"
fi
```

```bash
 3 < 5 == TRUE
```

letは比較演算子が真の時0を、偽の時1を、終了ステータスから得られます。
結果は以下のようになります。


    $ (( 3 < 5 )); echo $?
    > 0
    $ (( 3 > 5 )); echo $?
    > 1
    $ (( 3 + 3 )); echo $?
    > 0
    $ (( 3 - 3 )); echo $?
    > 1
    $ (( 0 )); echo $?
    > 1
    $ (( 1 )); echo $?
    > 0
    $ (( -1 )); echo $?
    > 0

比較演算子は、真であれば0を、偽であれば1を取得します。
比較しない場合、単に計算結果の場合は、結果が0であれば1を、0以外であれば1になるようです。

---


こんどは$(())を見ていきます。

    $ echo $(( 3 < 5 ))
    > 1
    $ echo $(( 3 > 5 ))
    > 0

$(())による値の取得は、比較演算子に成功すると1を、失敗すると0を返します。
取得しているのは終了ステータスで無い点に注意してください。
真であれば0ではなく1を取得してます。
この辺はこんがらないように。

ではbcコマンドは？

    $ echo $( echo " 3 < 5 " | bc);
    > 1
    $ echo $( echo " 3 > 5 " | bc);
    > 0

こちらも同じです。

これらを利用して、

```bash
function func()
{
    # $1 >= 50 の結果を反転。
    return $((! ($1 >= 50) ))
}

if func 33; then
    echo "50点以上"
else
    echo "50点未満"
fi


if func 66; then
    echo "50点以上"
else
    echo "50点未満"
fi
```

```txt
50点未満
50点以上
```

$(())やbcの結果を終了ステータスに利用
真は1を返すので、終了ステータスは!を使って反転させています。