シェルスクリプトの基本

# コメント

# 下記のように改行すると複数のコマンドを順番に実行できる
ls
cat test.txt

# ; を使うと複数のコマンドを一行仁鶴ことができる
ls;cat test.txt

# 改行は「\」を使用する(パイプライン「|」を使用する場合は、「|」の直後に改行をいれてもいい)
cat \
test.txt

# 変数(参照するには$を使用)
# 変数名は英語、数字、「_」のみ使用可能
var='1'
echo $var

# コマンドか変数か不明になる場合は変数展開を使用する
file_name='test'
cat {$file_name}.txt

# コマンド置換
# 現在の日付のファイルを作成
file_name=$(data '+%Y-%m-%d')
touch "$file_name"

# 位置パラメーター
# シェルスクリプトの引数に「*」を使用した場合はカレントディレクトリのファイルが挿入される
# シェルスクリプトの引数をシェルスクリプト内で取得するには、$0、$1、$2...を使用する
# 引数の個数は$#で取得できる
# 引数全体を取得する場合は、$@あるいは、$*を使用する

# ifの使い方
if [ "$1" = "test" ]; then
  echo "TRUE"
elif [ "$2" != "test"  ]; then
  # 文字列の評価
  # = : 等しい
  # != : 等しくない
  # -n str : strは空文字ではない
  # -z str : strは空文字である
elif [ "$3" -lt 0  ]; then
  # 数値の評価(整数にしか使えない)
  # -eq : 等しい
  # -ne : 等しくない
  # -lt : <
  # -le : <=
  # -gt : >
  # -ge : >=
elif [ -e ./test.txt  ]; then
  # ファイル属性の評価
  # -e file : fileは存在しない
  # -d file : fileはディレクトリとして存在する
  # -f file : fileはファイルとして存在する
  # -r file : fileは書き込み権限がある
  # file1 -nt file2 : file1はfile2より更新時刻が新しい
elif [ "$4" = 'test' -a "$5" = 'test' ]; then
  # AND演算子
elif [ "$4" = 'test' -o "$5" = 'test' ]; then
  # OR演算子
elif [ \( "$4" != 'test' -o "$5" != 'test' \) -a "$6" != 'test' ]; then
  # 条件の結合には()を使用する(ただし、エスケープが必要)
else
  echo "FALSE"
fi

# 条件式の省略
# test.txtがあれば、testと出力する
[ -f test.txt ] && echo "test"
# test.txtがなければ、textと出力する
[ -f test.txt ] || echo "test"
# ifをこのようにも書ける
if [ "$1" = "test" ] && [ "$2" = test ]; then
  echo "test"
fi

# $?でコマンドの終了ステータスを取得(入力0、標準出力1、標準エラー2)
# grepやlsなどのコマンドは、すべて終了時に終了ステータスが排出される
ls /test/
echo "status = $?"

# 自作のシェルスクリプトも同じように終了ステータスを出力できる
# 終了ステータス1で終了する場合
# 何も指定しない場合は最後に実行されたコマンドの終了ステータスになる
exit 1
# この後のコマンドは実行されない(エラー処理などに使用)

# forの使い方
# a、b、cと出力されるサンプル
for list in a b c
do
  echo $list
done

# 数値列を扱う例
# seqという数値列を順に出力するコマンドを使用する
for i in $(seq 1 5)
do
  echo $i
done

# すべてのコマンドライン引数をechoする例
for param in "$@"
do
  echo $param
done

# caseの使い方
case "$1" in
  *.txt)
    echo 1
    ;;
  *.js|*.css)
    # |でorを示す
    echo 2
    ;;
  *)
    # それ以外を示す
    ;;
esac

# while
i=10
while [ "$i" -le 10 ]
do
  echo "$i"
  # 算術式展開を利用
  # i=`expr $i + 2`よりもexprという外部コマンドを使用しないため高速
  i=$((i + 2))
done

# シェル関数
func ()
{
  echo 'test'
  # 終了ステータスを指定
  # 何も指定しない場合は最後に実行されたコマンドの終了ステータスになる
  return 1
  # この後のコマンドは実行されない(エラー処理などに使用)
}

# シェル関数を実行
func

シェルスクリプトのサンプル

$ vi sample.sh
// #!/bin/bash はファイルの先頭が「#!」であれば、その後ろのコマンド「/bin/bash」を実行するという意味。つまりbashで実行するという意味になる。
// 「#!」はシバン、あるいはシェバンと呼ばれる
------------ホームディレクトリのファイル使用容量を表示------------
#!/bin/bash
du -h ~ | tail -n 1
------------------------
// 実行権限を付与
$ chmod +x sample.sh
// シェルスクリプトの実行
$ ./sample.sh
// シバンがないシェルスクリプトの場合はsourceコマンド(ファイルからコマンドを読み込んで実行)を使う(この場合実行権限は不要)
$ source ./sample.sh
// bashにおいては、「.」コマンドとsourceコマンドは一緒のため、同じ実行結果となる
$ . ./sample.sh
// シバンがない場合、シェルを指定しても同じ動作となる
$ bash ./sample.sh

sourceコマンドの場合は現在のシェルで実行されるため、エイリアスの設定などは引き継がれるが、シェルスクリプトをそのまま実行した場合などはサブシェル(現在のシェルから新しく起動される子プロセスのシェル)で実行されるため、エイリアスの設定などは引き継がれずエラーとなることがあるため注意。

シェルスクリプトのTIPS

IFS(Internal Field Separator)の変更

IFSは環境変数で、bashが単語の区切りとして解釈する文字が格納されている。
IFSのデフォルト値はスペースとタブ、改行の3つである。
これは通常の環境変数のように変更可能だが、それ以降のコマンドにも影響するため、IFSを変更後にすぐにリセットすることが望ましい。

_IFS=$IFS
IFS=$'\n'
〜〜〜〜
IFS=$_IFS

~/.bash_profileにかかれているやつ

if [ -f ~/.bashrc ]; then
   source ~/.bashrc
fi

コマンドとしてひと通りの機能があるスクリプト

#!/bin/bash

usage()
{
  cat << END
Usage: ヒアドキュメントでヘルプを記述
END
}

if [ "$1" = "error" ]; then
  # エラーがある場合は標準エラーを出して、終了ステータスを2で終了
  echo 'error' 1>&2
  exit 2
fi

シェルスクリプトを書いたらすること

自分のシェルスクリプト置き場を作成し、その場所をサーチパスに登録する(登録しないとパスを指定しなければならなくなる)。

// シェルスクリプト置き場を作成(~/bin に作るのが慣例)
$ mkdir ~/bin
// サーチパスに~/binを追加
$ vi ~/.bash_profile
---------
# /etc/profileに記述されている$PATHの末尾に~/binを追加
PATH="$PATH:~/bin"
---------
// 反映
$ source ~/.bash_profile
// ~/bin/test.shがある場合は下記のように実行できるようになる
$ test.sh