Shell Script入門2 ~bash~

入門1はこちら

このページでは,行の先頭に>とある行は自分が入力したもの,
ないものは出力されたものとして書いています.
自分で試すときには>を取り除いて入力してください.


変数

多くのプログラミング言語と同じようにshellにも変数があります.
$PATH$SHLVLのように$マークから始まるという特徴があります.
上の2つは初めから定義してあることがほとんどですが,自分で変数を作ることもできます.

> i=256
> echo $i
256
> i=3+4
> echo $i
3+4
> i="test"
> echo $i
test

## このように数字も文字列として代入されてしまうので,数値を数値として
## 代入したいときには以下のようにする

> i=$((2))
> echo $i
2
> i=$((2+3))
> echo $i
5
> ((i++))
> echo $i
6
> echo $((i++))  # iを出力してからiをインクリメント
6
> echo $i
7

おまけ:シェル変数と環境変数


コマンド引数

C言語と並べて書くとすぐわかると思います.

#include <stdio.h>

int main(int argc, char *argv[]) {
    printf("argv[0]: %s\n", argv[0]);
    printf("argv[1]: %s\n", argv[1]);
    printf("argv[2]: %s\n", argv[2]);
    printf("argv[3]: %s\n", argv[3]);
    return 0;
}
> ./a.out coffee tea amazake
argv[0]: ./a.out
argv[1]: coffee
argv[2]: tea
argv[3]: amazake

つぎにシェルスクリプトでこれをやります.

#!/bin/bash
echo '$0': "$0"
echo '$1': "$1"
echo '$2': "$2"
echo '$3': "$2"
> ./sc.sh coffee tea amazake
$0: ./sc.sh
$1: coffee
$2: tea
$3: amazake

while文

これもC言語と並べて書いてみます.

#include <stdio.h>

int main(void)
{
    int i = 0;
    while (i < 10) {
        printf("%d\n", i);
        i++;
    }
    return 0;
}
> ./a.out
0
1
2
3
4
5
6
7
8
9

続いてシェルスクリプト

#!/bin/bash

i=0
while [ $i -lt 10 ]
do
    echo $i
    ((i++))
done
>./sc.sh
0
1
2
3
4
5
6
7
8
9

-ltというのはless thanという意味です.

と書きます.


for文

個人的にbashではよくfor文を使います.

例えば,複数のテキストファイルのバックアップを取りたいときに使います.

> ls
test1.txt test2.txt backup.sh
> ./backup.sh
> ls
test1.txt test2.txt test.old test2.old backup.sh

backup.shの中身はこうなっています.

for file in *.txt
do
    cp $file ${file%txt}old  
    # ${file%txt}で$file(hogehoge.txt)の拡張子を削除(hogehoge.になる)
done

*.txtに一致したものを一つずつ$fileに代入して,do以下の処理を実行しています.
慣れてくると,わざわざファイルを作るのが面倒になるのでTerminalに直接下のように打ち込んで使うことが多くなります.

> for file in *.txt; do cp $file ${file%txt}old; done;

また,C言語でよく使うようにfor文でループ回数を決めてループさせたいときは以下のようにします.

for i in {1..10}
do
    echo $i
done

これは,C言語で書くと下のようになります.

for (int i = 1; i <= 10; i++)
    printf("%d\n", i);

実行結果は,こうなります.

1
2
3
4
5
6
7
8
9
10

このfor文は,bashのブレース展開というものを利用しています.

> echo {1..10}
1 2 3 4 5 6 7 8 9 10
> # 2ずつ進めたいときはこうやります.
> echo {1..10..2}
1 3 5 7 9
> echo {1..10..5}
1 6
> echo hoge{1..3}
hoge1 hoge2 hoge3
> # ブレース展開で下のようなこともできます.
> echo hoge{.txt,.old}
hoge.txt hoge.old

バグに悩まされないための豆知識

bashのデフォルトでは未定義の変数を参照してもエラーが出ないので,ミスタイプなどによって,意図しない動作に悩まされることがあります.また,デフォルトではコマンドの終了時ステータスを見ていないので,例えばファイルを扱うスクリプトでファイルオープンが失敗したとしてもそのままスクリプトが実行されてしまいます.
それらを防ぐために,set -euコマンドを使います.

> echo $i

> # ↑何も表示されない スクリプトなら処理継続
> set -eu
> echo $i
bash: i: unbound variable
> # スクリプトならここでエラー落ちする
> set +eu
> echo $i

> # +で元に戻る

また,シバン(#!/bin/bash)に-euを書き足しても同じことができます.

#!/bin/bash -eu
echo $i
echo hogehoge

これを実行するとhogehogeが表示されずにecho $iでエラー落ちします.

-eをすることによってエラーが有り次第,そこでスクリプトを打ち止めにしてくれます.
-uは未定義の変数を使ったときにスクリプトを打ち止めにしてくれます.
また,バグで悩んだときには-xでデバッグ情報を出力して変数を中身を確認するなどできます.

Tweet