シェルスクリプトの勉強 - LPIC対策(シェルとスクリプト)

### シェルスクリプト

bashをはじめとして、シェルにはスクリプト言語によるプログラミング機能も備わっています。
シェルスクリプトは、テキストファイルにコマンドを記述するだけで実行することが出来ます。

例を見てみます。
ここでは、単純に、`/varに移動`して、`今いるディレクトリを表示`して、`/varのファイル数を表示`して、`ホームディレクトリにもどる`というスクリプトを書いてみました。

```bash:cntvar
$cat cntvar 
cd /var
pwd
ls -la | wc -l
cd ~/
pwd

# スクリプトの実行は、sourceか.を使う
$source cntvar 
/var
23
/home/riki


$. cntvar 
/var
23
/home/riki

# 実行権限を与えると、ファイル名の指定だけで実行出来る
$chmod a+x cntvar 
$./cntvar 
/var
23
/home/riki

# ファイルだけしていしても実行出来ないのはパスが通ってないから
$cntvar
-bash: cntvar: コマンドが見つかりません

# ホームディレクトリにパスを通してみる
$PATH="$PATH":~/

# 実行出来ました
$cntvar
/var
23
/home/riki
```


#### スクリプトに渡す引数

|変数名|説明|
|:---|---:|
|$0|シェルスクリプトのファイル名(フルパス)|
|$1|1番目の引数|
|$2|2番めの引数。以下順に、$3,$4,,,,$nとなる|
|$#|引数の数|

例を見てみます。
引数を表示するだけのスクリプトを作ってみました。

```bash:showarg
$cat showarg 
echo $0
echo $1
echo $2
echo $#

# ファイルに実行権限を与えて実行
$chmod a+x showarg 
$./showarg one
./showarg　 # ファイル名
one　　　　　# 1番目の引数
　　　　　　 # 2番目の引数はないから空
1　　　　　　# 引数のかずは"one"だけなので1個
```

因みに、シェル変数には`$?`という実行結果の戻り値を保存してる変数がある。

例を見たほうが早いです。

```bash
$ls aaaaaaaaaa
ls: cannot access aaaaaaaaaa: そのようなファイルやディレクトリはありません
$echo $?
2
$echo $?
0
```

一回目の`ls`で存在しないファイル名を指定してます。
その実行結果がエラーだったということを`$?`は保存してるので`2`と表示されました。
次の`echo $?`は正常に終了してるので`0`が表示されてます。
実行結果を見て次の処理を決める時とかに役立つのかも。

#### ファイルのチェック

ファイルが存在するかどうかでスクリプトの動きを変えるときなどに使います。

書式１： `test 条件文`
書式2： `[ 条件文 ]`

実行結果は、trueなら`0`を、falseなら`0以外`を返します。

条件式には色々な書式がありますが、多いので代表的なのだけ紹介します。

**ファイル形式のテスト**

|条件式|実行結果|
|:---|---:|
|-f ファイル|ファイルがあれば真|
|-f ディレクトリ|ディレクトリがあれば真|
|-r ファイル|ファイルが存在し、かつ読み込み可能なら真|
|-w ファイル|ファイルが存在し、かつ書き込みが可能なら真|
|-x ファイル|ファイルが存在し、かつ実行可能なら真|
|-s ファイル|サイズが0より大きいファイルがあれば真|
|-L ファイル|シンボリックリンクなら真|

**数値のテスト**

|条件式|実行結果|
|:---|---:|
|数値1 -eq 数値2|数値1と数値2が等しければ真|
|数値1 -ge 数値2|数値1が数値2以上なら真|
|数値1 -gt 数値2|数値1が数値2より大きければ真|
|数値1 -le 数値2|数値1が数値2以下なら真|
|数値1 -lt 数値2|数値1が数値2より小さければ真|
|数値1 -ne 数値2|数値1と数値2が等しくなければ真|

**文字列のテスト**

|条件式|実行結果|
|:---|---:|
|-n 文字列|文字列の長さが0よりも大きければ真|
|-z 文字列|文字列の長さが0なら真|
|文字列1 = 文字列2|文字列1と文字列2が等しければ真|
|文字列1 != 文字列2|文字列1と文字列2が等しくなければ真|

**テストの論理結合**

|条件式|実行結果|
|:---|---:|
|!条件|条件式が偽であれば真|
|条件1 -a 条件2|両方が真なら真(AND)|
|条件1 -o 条件2|どちらからが真なら真(OR)|

早速例を見ていきます。

```bash
$cat iftest1.sh 
# ファイルの存在チェック
if test -f testscript
then
  source ./testscript
else
  echo "testscript file not exists"
fi

# ファイル実行権限のチェック
if test -x catln
then
  catln
else
  echo "catln cannot execute"
fi

$. iftest1.sh 
testscript file not exists
lrwxrwxrwx   1 *** webadmin    12 12月 10 08:27 2014 hoge_sin.txt -> tmp/hoge.txt
num1 != num2
```

#### case文による条件分岐

書式

```bash
case 条件式 in
  値1) 実行文1 ;;
  値2) 実行分2 ;;
  ..
```

例

```bash
$cat casetest.sh 
case $1 in
  "初音ミク") echo "ボーカロイド" ;;
  "浜崎あゆみ") echo "歌姫" ;;
  "ホワイトベリー") echo "夏祭り" ;;
  "AKB"|"SKE") echo "秋元康" ;;
  *) echo "該当なし" ;;
esac

$. casetest.sh AKB
秋元康
$. casetest.sh ホワイトベリー
夏祭り
$. casetest.sh hoge
該当なし
```

#### for文による繰り返し

**書式**

```bash
for 変数名 in 変数に代入するリストの値のリスト
do
  実行文
done
```

例を見ます。

```bash:fortest.sh
$cat fortest.sh 
for air in 羽田 成田 大阪 関西国際
do
    echo $air空港
done

$. fortest.sh 
羽田空港
成田空港
大阪空港
関西国際空港
```

**因みに、`seq`コマンドを使うと連続した数値を自動生成出来て便利です**

```bash:fortest.sh
for num in `seq 10 20`
do
    echo $num人目
done
$. fortest.sh 
10人目
11人目
12人目
13人目
14人目
15人目
16人目
17人目
18人目
19人目
20人目
```

#### while文による繰り返し

for文以外にも繰り返し出来る処理があり、それが`while`です。
`while`の場合は、条件文が満たされてる間はdoとdoneの間の実行文を実行します。

```bash:whiletest.sh
$cat whiletest.sh 
i=1
while [ $i -le 10 ]
do
  echo $i
  let i=i+1
done
$. whiletest.sh 
1
2
3
4
5
6
7
8
9
10
```

#### readコマンド

`read`コマンドを使うと、シェルスクリプト内で標準入力からの入力を受け付ける際につかうT個が出来ます。
試しに、ユーザ名を入力すると、その名前で挨拶するスクリプトを作ってみます。

```bash:readtest.sh

$cat readtest.sh 
echo -n "お名前を入力して下さい: "
read username
echo "こんにちわ $usernameさん"

$. readtest.sh 
お名前を入力して下さい: 野獣岡野
こんにちわ 野獣岡野さん
```

ここでは、echoに`-n`オプションをつけて、改行しないようにしています。