シェルスクリプト

シェルスクリプトは、シェルコマンドをファイルに羅列して、一括に実行するものである。

本講義では、bash を元に説明を行う。

記述例

以下に、.cc ファイルのそれぞれ、行数と、総行数を表示するシェルスクリプトの例を示す。

#!/usr/bin/env bash
sum=0
for i in *.cc
do
  lines=`wc -l $i | awk '{print $1}'`
  echo $i: $lines
  sum=`expr $sum + $lines`
done
echo sum: $sum

変数

シェル変数

そのシェルの中でだけ利用される変数
上記の例におけるsumや、linesはシェル変数である。

シェル変数は、変数名=値 の形式で記述する。

値を取得する場合には、$変数名 で指定する。

環境変数

呼び出された子プロセスにも引き渡される変数
コマンド実行パスを示すPATHなどが例になる。

環境変数は、 export 変数名=値 の形式で記述する。
また、 変数名=値 コマンド と記載することで、指定したコマンドに対して 環境変数を渡すことが出きる。

値を取得する場合には、$変数名 で指定する。

組込変数

シェルには、いくつかの組み込み変数が存在する。 ここでは、シェルスクリプトで良く使われる組み込み変数を示す。

変数名 用途
${数値} シェルスクリプトにて指定された引数を示す。
$1 には、1番目の引数が格納されている。
$# シェルスクリプトで指定された引数の数を示す。
$@ シェルスクリプトで指定された引数の一覧をスペース区切りで示す。
$0 実行しているシェルスクリプト名
$? 直前に実行したコマンドの終了ステータス
$$ 実行してるシェルスクリプト自身のプロセスID

制御文

シェルスクリプトでは、処理の流れを指定する制御文が用意されている。

if文

シェルスクリプトでは以下の構文によって処理の分岐を実行できる。

  if {コマンド}
  then
    {真判定での実施コマンド}
  else
    {偽判定での実施コマンド}
  fi

ifで指定したコマンドの終了ステータスが0の場合に、真判定での実施コマンドが実行され 終了ステータスが0以外の場合に、偽判定の実施コマンドが実行される。

以下の記載で多重分岐の記述も可能

  if [ 条件1 ]
  then
    処理1
  elif [ 条件2 ]
  then
    処理2
  else
    処理3
  fi

上記は、もし「条件1」に当てはまるなら、「処理1」を実行する。 それ以外で「条件2」に当てはまるなら「処理2」を実行する。 それ以外は「処理3」を実行するという意味である。

なお、 if文の横に指定してる [ ] は test コマンドと呼ばれるもので、 シェルスクリプト内での論理条件を記述するために用意されているコマンドである。

以下に良く使われる。 test コマンドの記述をしめす。

記述方式 判定内容
[ 値1 = 値2 ] 値1 と 値2 が同じ文字列の場合に真
[ 値1 != 値2 ] 値1 と 値2 が異なる文字列の場合に真
[ -n 値 ] 値が1文字以上の文字列
[ -z 値 ] 値が空文字列
[ 値1 -eq 値2 ] 値1 と 値2 が同じ整数値の場合に真
[ 値1 -ne 値2 ] 値1 と 値2 が異なる整数値の場合に真
[ 値1 -lt 値2 ] 値1 が 値2 より小さい場合に真
[ 値1 -le 値2 ] 値1 が 値2 以下の場合に真
[ 値1 -gt 値2 ] 値1 が 値2 より大きい場合に真
[ 値1 -ge 値2 ] 値1 が 値2 以上の場合に真
[ ! 条件式 ] 条件式の結果が 偽 の場合に真
[ 条件式1 -a 条件式2 ] 条件式1、 条件式2 のいずれも真の場合に真
[ 条件式1 -o 条件式2 ] 条件式1、 条件式2 のいずれかが真の場合に真
[ -e パス ] パスのエントリが存在すれば真
[ -f パス ] パスのエントリが存在してレギュラーファイルであれば真
[ -d パス ] パスのエントリが存在してディレクトリであれば真
[ -L パス ] パスのエントリが存在してシンボリックリンクであれば真
[ パス1 -nt パス2 ] パス1の更新日時がパス2よりも新しければ真
[ パス1 -ot パス2 ] パス1の更新日時がパス2よりも古ければ真

case 文

シェルスクリプトでは文字列のパターンマッチによる処理分岐を以下の構文によって行う。

  case {文字列} in
    {パターン1})
      {処理1}
      ;;
    {パターン2})
      {処理2}
      ;;
    {パターン3})
      {処理3}
      ;;
  esac

case文では、指定された文字列がパターンに合う場合に、対象の処理が実行されます。
パターンマッチングは上から順に行なわれ、シェルのワイルドカードも指定することが可能である。 このため、 どのパターンにも正合しない場合の処理のパターンは *) を指定することで行う。

また、パターンを | で繋ぐことで、複数のパターンでのマッチングを記述することが可能である。 たとえば、 aaa|bbb) と指定することで 文字列が aaa もしくは bbb とマッチした場合の 処理分岐が行える。

for文

シェルスクリプトではリストを繰り返し実行するために以下の構文をつかう

  for {シェル変数} in {リスト}
  do
    {処理}
  done

do〜doneのなかでは、for で指定したシェル変数が、リスト順に変更されなが、リスト件数分実行されます。 リストの区切り文字は、シェル変数 IFS に指定されている文字が使用されます。

また、bash では、 {開始..終了} で連番のリストを生成できます。

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

上記の処理で 1 〜 10 が順に表示される

while文

コマンド結果が真の間繰り返しを行う構文は以下となる。

  while {コマンド}
  do
    {処理}
  done

コマンドの結果が真の場合に処理が実行される。

コマンドには、test コマンドが一般的に使われる。 他にかなず真を返す true や、 偽を返す false がある。

until文

コマンド結果が偽の間繰り返しを行う構文は以下となる。

  until {コマンド}
  do
    {処理}
  done

コマンドの結果が偽の場合に処理が実行される。

サブシェル

シェルスクリプトには、別環境を疑似的に実現するため、サブシェルといった機能がある。

  tar cvf - * | (cd ../new; tar xvf -)

上記のスクリプトでは、 括弧の中でディレクトリ移動をしているが括弧の中実行後に 元の環境に戻るので、全体として記述が楽になります。

なおサブシェルにはシェル変数などの値は引き継がれます。

文字列展開

シェルスクリプトでは、引用符によって文字列の展開内容が変化する。

以下に、各引用符による展開内容の違いを示す。

ダブルクオート (")

以下に変数展開についての一覧を示す。

展開記述 概要
$val valという変数の展開を行う
${val} valという変数の展開を行う
${val:-default} valという変数の展開を行う
valに値が無ければ default を展開する
${val:=default} valという変数の展開を行う
valに値が無ければ default を展開し、かつ、変数へ代入する
{val-default} valという変数の展開を行う
valが未定義であれば default を展開する
(定義があって値がないばあいは空を展開する。)
${val=default} valという変数の展開を行う, valが未定義であれば default を展開し、かつ、変数へ代入する
(定義があって値がないばあいは空を展開する。)
${val:?} valという変数の展開を行う
valに値が無ければエラーとなる
${val?} valという変数の展開を行う
valが未定義であればエラーとなる
${val:?error} valという変数の展開を行う
valに値が無ければエラーとなり、error がメッセージとして表示される
${val?error} valという変数の展開を行う
valが未定義であればエラーとなり、error がメッセージとして表示される
${val:offset:length} valという変数のoffsetからlength文字を展開する
lengthの指定がない場合には末尾までが対象となる

シングルクオート (')

バッククオート (`)

ヒアドキュメント

シェルスクリプトでは、複数行のドキュメントを表現するのに ヒアドキュメントという記述方法を利用する。

ヒアドキュメントの記述方法を以下にしめす。

cat >>EOS
hoge
fuge
piyo
EOS

コマンドの記述ないに >>{ラベル} と指定することで、次行から{ラベル}の手前までを ドキュメントとして扱われます。

変数展開やエスケープシーケンスもそのまま利用できます。

シェルスクリプトで良く使われるコマンド群

find

findコマンドは、指定したディレクトリ配下から条件に合致するファイルを検索して、 指定した処理を実行します。

■ コマンド概要

find [-H | -L | -P] path ... [expression]

path で指定したディレクトリ配下を再帰的に検索して、expression 指定した処置をおこなう。

■ オプション

■ expression

以下によく使われる expression を示す。

探索条件

expression 用途
-name pattern エントリ名がpatternのエントリを検索する。 patternにはシェルのワイルドカードも指定することが可能である。
-type type typeで指定したタイプのエントリを検索する。 typeに指定されるのは以下の通り
d ディレクトリ
f 通常のファイル
l シンボリックリンク
s ソケット p 名前付きパイプ (FIFO)
-regex pattern エントリ名が正規表現のpatternにマッチするエントリを検索する。
-atime n エントリの参照日時が n 日前のエントリを検索する。 (起点は前日になる)
-ctime n エントリの変更日時が n 日前のエントリを検索する。 (起点は前日になる)
-mtime n エントリの更新日時が n 日前のエントリを検索する。 (起点は前日になる)
-user uname 所有者が uname なエントリを検索する。
-group gname 所有グループが gname なエントリを検索する。
-readable 読み取り権を持つエントリを検索する。
-writable 書き込み権を持つエントリを検索する。
-executable 実行権を持つエントリを検索する。

論理式

expression 用途
! expr expr の条件が偽となるエントリを検索する。
expr1 -a expr2 expr1, expr2 いずれの条件にも当て嵌まるエントリを検索する。
expr1 -o expr2 expr1, expr2 いずれかの条件にも当て嵌まるエントリを検索する。

アクション

expression 用途
-print 条件にマッチしたエントリを改行で区切って表示する。
-print0 条件にマッチしたエントリをヌル文字で区切って表示する。 (xargs -0 に対応)
-exec command ; 検索されたファイルに対して ファイル毎に command を実行する。 {} の位置に対象のファイルのパスが展開される。
-exec command {} + 検索されたファイルに対して、 command を実行する。 コマンドは一度の実行として扱われ、{} に検索されたすべてのファイルが展開される。

■ 使用例

./src 配下のソースファイルとヘッダファイルの更新日時を今の時間に変更する例を示す。

find -L ./src  \( -name "*.[ch]" -o -name "*.[ch]pp" \)  -print -exec touch \{\} \;

awk

awkコマンドは、標準入力や、ファイル内の文字列を1行ずつ、指定されたプログラムにて処理をおこなう。

シェルスクリプトでは、区切り文字で分割された各フィールドに対する処理を行うツールとして利用される。

■ コマンド概要

awk [ -F fs ] [ -v var=value ] [ 'prog' | -f progfile ] [ file ... ]

標準入力 もしくは、file で指定したテキストファイルに対して prog で指定した処理を行う。

■ オプション

■ 処理記述

awk に指定する処理は、以下のフォーマットで記述を; もしくは、 改行 で区切って指定する。

pattern { action }

pattern には、処理対象とする行を特定するためのパターンを指定する。
action には、pattern でマッチした行に対して実行する処理を awk programing で記述する。
pattern が未指定の場合には、全ての行に対して action を実行する。

awk では、対象行の各フィールドをフィールド変数で参照することが出来る。たとえば $0は 行全体を差し、 $1, $2 はそれぞれ第1フィールド、第2フィールドの値を返す。

例えば、

awk -F "," '$2 < $3 {print $0}' < input.txt

と指定すると第2カラム目が、第3カラム目より小さい行を表示することになる。

pattern, action の記述方法については、awk のmanデータを参照のこと

以下にシェルスクリプトで使う pattern 記述と action で使用するコマンド について示す。

■ pattern 指定について

pattern 概要
/regix/ regix に指定された正規表現とマッチする行を対象とする。
/start/,/end/ start とマッチする行から、 end にマッチする行までを対象とする。
比較式 比較式に指定された条件式にマッチする行を対象とする。 比較式には length のようなコマンドの指定も可。
BEGIN 1行目の実行前に指定のアクションを実行する。
END 最終行実行後に指定のアクションを実行する。

■ よく使うコマンド

action では、変数や四則演算などが利用することができる。 また、以下のようなコマンドを利用することもできる。

コマンド名 用途
print string string に指定した内容を標準出力に出力する。
length(string) string に指定した内容の文字数を取得する。
tolower(string) string に指定した文字列を小文字変換する。
toupper(string) string に指定した文字列を大文字変換する。

sed

sedは、標準入力やファイル内の文字列を1行ずつに対して、編集を行うことが出来る。 編集結果は標準出力に出力される。

■ コマンド概要

sed [-Ean] command [file ...]
sed [-Ean] [-e command] [-f command_file] [-i extension] [file ...]

標準入力 もしくは、file で指定したテキストファイルに対して command, command_file で指定した編集を行う。

■ オプション

■ コマンド記述

sedで指定するコマンドは [位置指定]編集コマンド のフォーマットで行う。

1,3s/abc/efg/g

上記の例では、 1,3 が位置指定で、s/abc/efg/g が編集コマンドになる。(1〜3行目の "abc" を "efg" に置換する)

位置指定には、カンマ区切りでの行範囲指定のほかに、//で囲んだ正規表現指定が可能。

以下に良く使う編集コマンドを示す。

編集コマンド 用途
s/regexp/replace/flag regexp の正規表現にマッチする文字列を replace に置換する。
flags には、以下のものを0個以上指定できる。
N パターンスペースで N 回目にマッチした regexp のみを置換します。
g 先頭だけではなく、重なりあわない全てのマッチした内容を replace で置換する。
p 置換が行われたら、パターンスペースの内容を標準出力に書き出す。もし、置換後の内容が置換前のものと同一でも置換が行われたとみなす。
d 指定した行の削除を行う。
c text 指定した行をtextに書き換える

expr

expr はコマンドラインを算術式と判断して、その計算結果を標準出力に返す。

bashやzsh だと $((演算式)) で記述できるので、利用するシェルが明確であるのであれば、そちらを使うのもあり。

例:(シェル変数 i のカウントアップ)

i=`expr $i + 1`

もしくは

i=$((i+1))

seq

seq は連続する数値列を標準出力に返す。

seq のコマンドパラメ=タは以下のとおり

seq [start [incr]] last

start には、開始値、incr には、 増分値 last には終了値を指定する。
start, incr の規定値は 1 である。

たとえば、

seq 2 2 12 

だと、以下の出力になる。

2
4
6
8
10
12

シェルスクリプトでは以下のように for文 で使われることが多い

for i in `seq 1 4`
do
  echo $i
done

bash, zshなどでは、 {1..4} で同様の記述が可能です。