Shellの基本を学ぶ（３）制御構文

# if

構文は下記の通り

`if TEST-COMMANDS; then CONSEQUENT-COMMANDS; fi`

* [7.1. Introduction to if](https://www.tldp.org/LDP/Bash-Beginners-Guide/html/sect_07_01.html)

コマンドの実行結果が成功したか失敗したかによって結果を変える。改行を表すには、`;` を使う。

```bash
$ if mkdir b; then echo "ok"; else echo "error"; fi
ok
$ if mkdir b; then echo "ok"; else echo "error"; fi
mkdir: b: File exists
error
```

残念ながら、まだ `[[ something ]]` の文法が正しく理解出来ていないが、理解したら追記する。

```bash
$ if [[ $note ]]; then
>   echo "$(date): $note"
> else
>   echo "No input"
> fi
No input
```

# リターンコード

0..255 であり、0が成功。他はエラーコード。シェルスクリプトは、リターンコードを返して終わる。

# Conditional Expression

* ファイルとディレクトリのテスト
* ストリングのテスト
* 数学的なテスト

## 構文

`[[ Expression ]]`

## NOTE

最後のサンプルは常に true になる。１つの引数と解析されるから。

Expression にはスペースが必要。

_ifsample_

```bash
#!/bin/bash

# Assign null
str=

if [[ $str ]]; then
  echo "have it"
else 
  echo "don't have it"
fi

if [[ $str = "some" ]]; then
  echo "I have some"
else 
  echo "I don't have some"
fi

if [[ $str="some" ]]; then
  echo "I have some"
else 
  echo "I don't have some"
fi
```

### 結果

```bash
./ifsample
don't have it
I don't have some
I have some
```

## 他の Expression

* `[[ -e $filename ]]` ファイルの存在チェック
* `[[ -d $dirname ]]` ディレクトリの存在チェック

### サンプル

すでに解説した構文で構成されている。`!` は　NOT の意味合い。必ずスペースを開けること。

_create_script_

```bash
#!/bin/bash

if [[ ! $1 ]]; then
  echo "Missing argument"
  exit 1
fi

scriptname="$1"
bindir="${HOME}/Codes/experiment/bin"
filename="${bindir}/${scriptname}"

if type "$scriptname"; then
  echo "There is already a command with name ${scriptname}"
  exit 1
fi

if [[ ! -d $bindir ]]; then
  if mkdir "$bindir"; then
    echo "created ${bindir}"
  else
    echo "Could not create ${bindir}."
    exit 1
  fi
fi

echo '#!/bin/bain' > "$filename"
chmod u+x "$filename"
if [[ $EDITOR ]]; then
  $EDITOR "$filename"
else
  echo "Script created; not starting editor because \$EDITOR is not set."
fi
```

### 実行結果

```bash
./create_script
Missing argument
$ ./create_script if
if is a shell keyword
There is already a command with name if
$ ./create_script ls
ls is /bin/ls
There is already a command with name ls
$ ./create_script try
./create_script: line 12: type: try: not found
created /Users/ushio/Codes/OpenHack/experiment/bin
```

# document

help コマンドで、`[[` や `test` を調べると仕様がわかる。test コマンドが実行されている。`[[ ... ]]` は bash の拡張。

```bash
$ help [[
[[ ... ]]: [[ expression ]]
    Returns a status of 0 or 1 depending on the evaluation of the conditional
    expression EXPRESSION.  Expressions are composed of the same primaries used
    by the `test' builtin, and may be combined using the following operators

    	( EXPRESSION )	Returns the value of EXPRESSION
    	! EXPRESSION	True if EXPRESSION is false; else false
    	EXPR1 && EXPR2	True if both EXPR1 and EXPR2 are true; else false
    	EXPR1 || EXPR2	True if either EXPR1 or EXPR2 is true; else false

    When the `==' and `!=' operators are used, the string to the right of the
    operator is used as a pattern and pattern matching is performed.  The
    && and || operators do not evaluate EXPR2 if EXPR1 is sufficient to
    determine the expression's value.
$ help test
test: test [expr]
    Exits with a status of 0 (true) or 1 (false) depending on
    the evaluation of EXPR.  Expressions may be unary or binary.  Unary
    expressions are often used to examine the status of a file.  There
    are string operators as well, and numeric comparison operators.

    File operators:

        -a FILE        True if file exists.
        -b FILE        True if file is block special.
        -c FILE        True if file is character special.
        -d FILE        True if file is a directory.
        -e FILE        True if file exists.
        -f FILE        True if file exists and is a regular file.
        -g FILE        True if file is set-group-id.
        -h FILE        True if file is a symbolic link.
        -L FILE        True if file is a symbolic link.
        -k FILE        True if file has its `sticky' bit set.
        -p FILE        True if file is a named pipe.
        -r FILE        True if file is readable by you.
        -s FILE        True if file exists and is not empty.
        -S FILE        True if file is a socket.
        -t FD          True if FD is opened on a terminal.
        -u FILE        True if the file is set-user-id.
        -w FILE        True if the file is writable by you.
        -x FILE        True if the file is executable by you.
        -O FILE        True if the file is effectively owned by you.
        -G FILE        True if the file is effectively owned by your group.
        -N FILE        True if the file has been modified since it was last read.

      FILE1 -nt FILE2  True if file1 is newer than file2 (according to
                       modification date).

      FILE1 -ot FILE2  True if file1 is older than file2.

      FILE1 -ef FILE2  True if file1 is a hard link to file2.

    String operators:

        -z STRING      True if string is empty.

        -n STRING
        STRING         True if string is not empty.

        STRING1 = STRING2
                       True if the strings are equal.
        STRING1 != STRING2
                       True if the strings are not equal.
        STRING1 < STRING2
                       True if STRING1 sorts before STRING2 lexicographically.
        STRING1 > STRING2
                       True if STRING1 sorts after STRING2 lexicographically.

    Other operators:

        -o OPTION      True if the shell option OPTION is enabled.
        ! EXPR         True if expr is false.
        EXPR1 -a EXPR2 True if both expr1 AND expr2 are true.
        EXPR1 -o EXPR2 True if either expr1 OR expr2 is true.

        arg1 OP arg2   Arithmetic tests.  OP is one of -eq, -ne,
                       -lt, -le, -gt, or -ge.

    Arithmetic binary operators return true if ARG1 is equal, not-equal,
    less-than, less-than-or-equal, greater-than, or greater-than-or-equal
    than ARG2.
```

# 数学的比較構文

`[[ arg1 OP arg2 ]]`

## よくあるオペレーション

* -eq: 等しい
* -ne: 等しく無い
* -lt: less than
* -gt: grater than

## 特殊な変数

* `$#` スクリプトの引数の数
* `$?` exit status
* `${#var} 変数の値の文字列長を知る

## サンプル

先に説明されているもののみで構成されている。`wc -l` 単語の数を数えるもの。

_filecounter_

```bash
#!/bin/bash

if [[ $# -ne 2 ]]; then
  echo "Two arguments required"
  exit 1
fi

if [[ ! -d $1 ]]; then
  echo "'$1' is not a directory"
  exit 1
fi
if [[ ! -d $2 ]]; then
  echo "'$2' is not a directory"
  exit 1
fi

dir1="$1"
dir2="$2"

count_1=$(ls -A1 "$dir1" | wc -l)
count_2=$(ls -A1 "$dir2" | wc -l)

if [[ $count_1 -gt $count_2 ]]; then
  echo "${dir1} has most files"
elif [[ $count_1 -eq $count_2 ]]; then
  echo "number of files is equal"
else
  echo "${dir2} has most files"
fi

exit 0
```

### 実行結果

b ディレクトリにより多くのファイルがある場合

```bash
$ ./filecounter a b
b has most files
```

_lengthcounter_

```bash
#!/bin/bash

if [[ $# -ne 2 ]]; then
  echo "Two arguments required"
  exit 1
fi

length_1=${#1}
length_2=${#2}

if [[ $length_1 -gt $length_2 ]]; then
  echo "$1 is longer than $2"
elif [[ $length_1 -eq $length_2 ]]; then
  echo "length is equal"
else 
  echo "$2 is longer than $1"
fi

exit 0
```

### 実行結果

```bash
$ chmod +x lengthcounter
$ ./lengthcounter abc abcd
abcd is longer than abc
$ ./lengthcounter abcd abc
abcd is longer than abc
$ ./lengthcounter abcd abcd
length is equal
```

# And, or not

## And 

`&&` 

`[[ $# -eq 1 && $1 = "foo" ]]`

## Or

`||`

`[[ $a || $b ]]`

ちなみに、`-a` と `-o` というスイッチがあるけど、古いので使わないようにとのこと。