初心者向けシェルスクリプトの基本コマンドの紹介

こんにちは、初心者向け基本コマンドを紹介しているシェルスクリプト入門です。
最後で、ファイル名を一発で変える簡単なシェルスクリプトを書いてみましょう。
*環境：Mac OSX El Capitan Terminal; bash / sh shell environment;*

#シェルスクリプトとは
- シェルスクリプトとは、簡単に言うとUnixコマンドなどを並んで実行するだけです。
- いつ何の条件で何の命令を実行するとか、ファイルコンテンツを読み込むとか、ログファイルを書き出すとかをする役割です。

#基本設定
`test.sh`ファイルを作成します。

```bash:test.sh
#!/bin/sh

echo "Hello, World!"
```

- シェルスクリプトファイルは基本的に`.sh`の拡張子で作成します。
- 実際のコードを書く前に最初の一行目は `#!/bin/sh`を書き、システムにこれからシェルスクリプトを書きますようと知らせるものです。（`#`は*hash*、`!`は*bang*ですから、`#!`を*shebang*と言います。）ただし bash の独自機能を使う場合は `#!/bin/bash` と書かなければなりません (参考: [bashコーディング規約](http://qiita.com/mashumashu/items/f5b5ff62fef8af0859c5)、["bashism" について思うこと](http://qiita.com/miyu/items/4fce490115272b87c887)。)
- シェルスクリプトファイル（test.sh）を実行するには、ターミナルからファイルが保存されてる場所で以下のどちらかのコマンドで実行できます。

```
$ chmod 755 test.sh
$ ./test.sh
````

```
$ sh test.sh
```

```
$ bash test.sh
```
ファイルを実行すると`Hello, World!`が表示されます。

#基本コマンド
## コメント
`#`を書いてコメントを書くことができます。

```bash:test.sh
#!/bin/sh

# これはコメントです
# echo "コメントは実行されません！"

echo "Hello, World!"
```

## 入力・出力
`echo`で出力、 `read`で入力します。

```bash:test.sh
#!/bin/sh

read NAME
echo "Hello, $NAME!"
```
実行結果、

```
$ ./test.sh
Tensai
Hello, Tensai!
```
入力を待っている時、スクリプトが続きません。
Bash では`-e`フラグで特殊テキストをエスケープできます。

```bash
#!/bin/bash
echo -e "Hello\n$NAME!" #改行されます
```

## 変数
- 変数の名前として半角英数字とアンダーバーが使えます。*aからz、AからZ、0から9と_*です。
- 変数に値を与える時`=`を前後空白なしで書きます。文字列な場合`"`で囲みます。
- 変数をアクセスする時変数名の前に`$`を入れます。あるいは`$`入れて変数を`{}`で囲みます。
- 一つの変数に一つの値しか保存できません。
- 変数の値を上書きされないようには`readonly`を使います。
- 変数を`unset`で削除することができます。（`readonly`変数を削除することができません。）

```bash:test.sh
#!/bin/sh

var="これは変数です"
VaR_2="これも変数です"
echo "Var_2=$VaR_2"

VaR_2="VaR_2が変更されました。"
echo ${VaR_2}

readonly var
var="readonly varを変えてみる。"
```
実行結果、

```
$ ./test.sh
Var_2=これも変数です
VaR_2が変更されました。
shell.sh: line 11: var: readonly variable
```

##特別な変数
シェルスクリプトでは以下の特別な変数があります。

|変数|機能|
|:--|:--|
|\$0|スクリプト名|
|\$1 ~ $9|引数、1番目の引数を`$1`、2番目の引数を`$2`でアクセスする|
|$#|スクリプトに与えた引数の数|
|$*|全部の引数をまとめて1つとして処理|
|$@|全部の引数を個別として処理|
|$?|直前実行したコマンドの終了値（0は成功、1は失敗）|
|$$|このシェルスクリプトのプロセスID|
|$!|最後に実行したバックグラウンドプロセスID|

```bash:test.sh
#!/bin/sh

echo "\$0（スクリプト名）: $0"
echo "\$1（1番目の引数）: $1"
echo "\$2（2番目の引数）: $2"
echo "\$#（引数の数）: $#"
echo "\"\$*\": \"$*\""
echo "\"\$@\": \"$@\""
VAR="exit値は0になるはずです"
echo $?
```
実行結果、

```
$ ./test.sh first second 3rd
$0（スクリプト名）: test.sh
$1（1番目の引数）: first
$2（2番目の引数）: second
$3（3番目の引数）: 3rd
$#（引数の数）: 3
"$*": "first second third"
"$@": "first second third"
0
```

###特殊文字
``* ? [ ' " ` \ $ ; & ( ) | ~ < > # % = スペース タブ 改行``　はシェルスクリプトの特殊文字です。文字列として使うときは `\` を書いてから使います。

##変数値の置換

|文法|説明|
|:--|:--|
|${var}|変数値を入り変えます|
|${var:-word}|変数がまだセットされていないか空文字列の場合*word*を返します。**varに保存しません**|
|${var:=word}|変数がまだセットされていないか空文字列の場合*word*を返します。**varに保存します**|
|${var:?word}|変数がまだセットされていないか空文字列の場合置換に失敗し、スタンダードエラーにエラーを表示します|
|${var:+word}|変数がセットされている場合*word*を返します。**varに保存しません**|

```bash:test.sh
#!/bin/sh

echo "1 - ${var:-wordSetInEcho1}"
echo "2 - var = ${var}"
echo "3 - ${var:=wordSetInEcho3}"
echo "4 - var = ${var}"
unset var
echo "5 - ${var:+wordSetInEcho5}"
echo "6 - var = $var"
var="newVarValue"
echo "7 - ${var:+wordSetInEcho7}"
echo "8 - var = $var"
echo "9 - ${var:?StandardErrorMessage}"
echo "10 - var = ${var}"
```
実行結果：

```
1 - wordSetInEcho1
2 - var = 
3 - wordSetInEcho3
4 - var = wordSetInEcho3
5 - 
6 - var = 
7 - wordSetInEcho7
8 - var = newVarValue
9 - newVarValue
10 - var = newVarValue
```

##配列 (Bash)

```bash:test.sh
#!/bin/bash

#bash shellで配列の書き方
ARRAY=(item1 item2 item3 item4)
ARRAY[0]="ITEM1"
ARRAY[2]="ITEM3"

echo "ARRAY[0]: ${ARRAY[0]}"
echo "ARRAY[1]: ${ARRAY[1]}"

#全てのアイテムをアクセスする
echo "ARRAY[*]: ${ARRAY[*]}"
echo "ARRAY[@]: ${ARRAY[@]}"
```
実行結果、

```
$ ./test.sh
ARRAY[0]: ITEM1
ARRAY[1]: item2
ARRAY[*]: ITEM1 item2 ITEM3 item4
ARRAY[@]: ITEM1 item2 ITEM3 item4
```
## オペレータ
shellでは算術演算子を`` `expr 数字 演算子 数字` ``で計算できます。

|演算子|意味|例|
|:--:|:--:|:--|
|+|加|``echo `expr 10 + 20` `` *=> 30*|
|-|減|``echo `expr 20 - 10` `` *=> 10*|
|\\*|乗|``echo `expr 11 \* 11` `` *=> 121*|
|/|割|``echo `expr 10 / 2` `` *=> 5*|
|%|剰余|``echo `expr 10 % 4` `` *=> 2*|
|=|指定|``a=$b `` *bの値はaに保存されます*|
|==|同|\[ "\$a" == "\$b" \] *\$aと\$bが同じ場合TRUEを返します。*|
|!=|異|\[ "\$a" != "\$b" \] *\$aと\$bが同じではない場合TRUEを返します。*|

|比較|意味|例|
|:--:|:--:|:--|
|-eq|イコール|``[ "$a" -eq "$b" ]`` *\$aと\$bが同じ場合TRUEを返します。*|
|-ne|異なる|``[ "$a" -ne "$b" ]`` *\$aと\$bが違い場合TRUEを返します。*|
|-gt|より大きい|``[ "$a" -gt "$b" ]`` *\$aが \$bより大きい場合TRUEを返します。*|
|-lt|より小さい|``[ "$a" -lt "$b" ]`` *\$aが \$bより小さい場合TRUEを返します。*|
|-ge|より大きいか同じか|``[ "$a" -ge "$b" ]`` *\$aが \$bより大きいか同じ場合TRUEを返します。*|
|-le|より小さいか同じか|``[ "$a" -le "$b" ]`` *\$aが \$bより小さいか同じ場合TRUEを返します。*|
|!|ではない|``[ ! "$a" -gt "$b" ]`` *\$aが \$bより大きくない場合TRUEを返します。*|
|-o|どちらか|``[ "$a" -gt "$b" -o "$a" -lt "$b" ]`` *\$aが \$bより大きいか小さいかの場合TRUEを返します。* (Bash拡張・POSIX廃止予定)|
|-a|両方|``[ "$a" -gt 90 -a "$a" -lt 100 ]`` *\$aが 90より大きく100より小さい場合TRUEを返します。* (Bash拡張・POSIX廃止予定)|
|-z|文字列が空か|``[ -z "$a" ]`` *\$aが何も指定してない場合TRUEを返します*|
|-n|文字列が空か|``[ -n "$a" ]`` *\$aに何かを指定しした場合TRUEを返します*|

※`[` コマンドの引数に変数を指定するときは `"` でクォートする必要があります

上記のオペレータを使って`if`条件を書きます。

##if 条件
- `if`の基本の書き方は `if [ 条件 ] then コマンド fi` です。
- 条件が真の場合 `then` の次のコマンドを実行します。
- 違う場合次々の `elif [ 条件 ]` を確認します。
- 真の条件がない場合 `else` の次のコマンドを実行して終了します。
- `else` がない場合は、そのまま終了します。

```bash:test.sh
#!/bin/sh

if [ "$1" -gt "$2" ]
then 
    echo "1番目の引数が2番目の引数より大きい"
elif [ "$1" -eq "$2" ]
then
    echo "1番目の引数と2番目の引数は同じです"
else
    echo "1番目の引数が2番目の引数より小さい"
fi
```
実行結果、

```
$ ./test.sh 2 7
1番目の引数が2番目の引数より小さい
$ ./test.sh 10 5
1番目の引数が2番目の引数より大きい
$ ./test.sh 9 9
1番目の引数と2番目の引数は同じです
```

##Switch 条件
- `switch`の基本の書き方は `case 変数 in 条件・値) コマンド ;; esac` です。
- 条件・値が変数と合う場合それの次のコマンドを実行します。

```bash:test.sh
#!/bin/sh

DRINK="coffee"
case "$DRINK" in
    "beer") echo "ビールです" 
    ;;
    "juice") echo "ジュースです" 
    ;;
    "coffee") echo "プログラマーが飲むとコードに変化！" 
    ;;
esac
```
実行結果、

```
$ ./test.sh
プログラマーが飲むとコードに変化！
```

##ループ
ループを、
- `break`キーワードで終了
- `continue`キーワードで現在のループを飛ばすことができます。
ことができます。

###while ループ
条件が合うときループします。

```bash:test.sh
#!/bin/sh

a=0
while [ $a -lt 5 ]
do
    echo $a
    a=`expr $a + 1`
done
```
実行結果：

```
$ ./test.sh
0
1
2
3
4
```

###until ループ
`while`の逆で、条件が合うまでループします。

```bash:test.sh
#!/bin/sh

a=0
until [ ! $a -lt 5 ]
do
    echo $a
    a=`expr $a + 1`
done
```
実行結果：

```
$ ./test.sh
0
1
2
3
4
```

###for ループ
- `for`の基本の書き方は `for 変数 in 複数値・変数・範囲 do コマンド done` です。
- 条件・値が変数と合う場合それの次のコマンドを実行します。

```bash:test.sh
#!/bin/sh

for var in 0 1 2 3 4  #範囲の書き方(Bash独自) => {0..4}
do
    echo $var
done
```
実行結果：

```
$ ./test.sh
0
1
2
3
4
```

##関数
シェルスクリプトでは、関数を書いて引用することができます。

```bash:test.sh
#!/bin/sh

#関数を指定します
MyFunction () { 
    echo "関数のechoです。"
}
MyParamFunc() {
    echo "引数1:$1 引数2:$2"
}

#関数を呼び出します
MyFunction
MyParamFunc param1 param2
```
実行結果：

```
$ ./test.sh
関数のechoです。
引数1:param1 引数2:param2
```

#おしまい
以上はシェルスクリプトの基本コマンドですが、それをunixコマンドと一緒に使えば効率的になります。unixコマンドも沢山ありますが、以下のサンプルにはファイル移動するコマンド`mv`と`expr`だけを使っています。

```bash:rename.sh
#!/bin/sh

#このスクリプトがあるディレクトリ中の
#txtファイルを全部mytxt{番号}.txtに
#変更するスクリプトです

index=1
for file in *.txt
do
    mv "$file" "mytxt${index}.txt"
    index=`expr $index + 1`
done
```

実行結果：
`ls`は現在いるディレクトリ中のファイルをリストします。

```
$ ls
aaa.txt		bbb.txt		ccc.txt		ddd.txt		eee.txt		test.sh
$ ./test.sh
$ ls
mytxt1.txt	mytxt2.txt	mytxt3.txt	mytxt4.txt	mytxt5.txt	test.sh
```

今日は以上になります。