Edited at

シェルスクリプト入門【ドットインストール】

More than 3 years have passed since last update.

ドットインストール様の動画を拝聴し、ShellScriptを学習しました。

その際のメモです。

動画の方はこちらから↓

http://dotinstall.com/lessons/basic_shellscript


1 シェルスクリプトとは?


シェルスクリプトは、オペレーティングシステムのシェルまたはコマンドラインインタプリタ向けに書かれたスクリプトである。

シェルスクリプト - Wikipediaより

シェル (shell) はオペレーティングシステム (OS) のユーザーのためにインタフェースを提供するソフトウェアであり、カーネルのサービスへのアクセスを提供する。

シェル - Wikipediaより


OSから情報を引き出したり、操作したりするスクリプトのこと。


2 はじめてのシェルスクリプト

今回、私はVagrant(CentOS)にて学習しました。

以下のコマンドで、その環境にて使用されているシェルを確認します。

echo $SHELL

# /bin/bash

私の環境では、bashが使用されていました。

学習用ディレクトリ内にhello.shを作成し、文頭に以下の1行を書きます。

これは、シェルスクリプトを書く際の決まり文句となる。

!#/bin/bash

Helloという文字列を出力します。

# 文字列の出力

echo "Hello"
# 正常終了した場合、0を返す
exit 0

実行の際には、ファイルのあるディレクトリで以下のコマンドを入力する

./hello.sh

# Hello


3 変数(文字列)

# =前後にはスペースを入れない

s="Hello"
echo $s # Hello
echo "$s" # Hello
echo "${s}" # Hello
echo $s$s # HelloHello
echo "$s $s" # Hello Hello
echo '$s' # $s シングルクォーテーションだと展開されない


4 数値演算

$x=10

echo `expr $x + 2` # 12
# バッククォートで囲われたコマンドが実行される
# expr、演算子の前後にはスペースを入れる

ちなみにexprは整数計算を行う際に利用されるシェルスクリプトのコマンドです。

整数計算を行う「expr」


5 四則演算

演算子は以下の通りです。

# 引き算: +

# 引き算: -
# 割り算: /
# 掛け算: \*
# 掛け算のみ、バックスラッシュでエスケープさせる。エスケープしないとシンタックスエラーとなる

また、数学でもそうですが、先に計算したい足し算、引き算には()でくくると思います。

その場合は、以下のように書きます。

シェルスクリプトでは、()はエスケープさせないといけません。

# (x + 2) * 5 を計算したい

echo \( $x + 2 \) \* 5

変数の定義時に、readonlyをつけると、上書きが効かなくなります。

大事な変数にはつけましょう。

readonly FILE_NAME=“test"

FILE_NAME=“test2”
# -bash: FIRST_NAME: readonly variable
# -> 上書きできないよ、というエラー


6〜7 配列

a=(2 4 6)       # 配列の定義

# 1番目の配列を取り出す
echo $a # 2

# 2番目の配列を取り出す([]内を変更すれば、任意の順番の配列を取得できる)
echo $a{[1]} # 4

# エラーの例({}をつけない場合)
# $aは配列の1番目として読み込まれ、[1]は文字列と認識されてしまう
echo $a[1] # 2[1]

# 全ての配列を出力
echo {$a[@]} # 2 4 6

# 配列の数をカウント
echo ${#a[@]} # 3

配列を使う上で、下記のようなことも覚えておく必要があります。

a=(2 4 6)

# 配列の上書き
a[2]=10 # 配列の3番目を10に上書きする

echo ${a[@]} # 2 4 10

# 配列の要素の追加
a+=(20 30) # 配列の要素を追加

echo ${a[@]} # 2 4 10 20 30

また、配列の応用例で、UNIXコマンドのdate関数を使い、

1週間の日付を配列で持ってきて、指定した位置の日付を出力させます。

# 日付を取得

d=(`date`)

# 4番目の値を取得(0が日曜日)
echo ${d[3]}
# 今週の水曜日が出力される


8〜9 条件式

testコマンドは、与えた条件が正しいかどうか試験してくれます。

[ ]で囲うことで省略も可能)

正常終了すると0が、エラーが起こると1を返します。

test 1 -eq 2; echo $?          1が返る

test 1 -eq 1; echo $? 0が返る

$?は直前にやった条件式の結果を返す標準変数です。

比較演算子や論理演算子については、下記リンクに詳しく述っています。

if文とtestコマンド


10~11 if文で条件分岐

if文は以下のように書きます。

# $xが60より大きかったら、OK!と出力

x=70
if test $x -gt 60
then
echo "OK!"
fi
# OK!

また、testは以下のように[]で書き換えることも可能です。

# $xが60より大きかったら、OK!と出力

x=70
if [ $x -gt 60 ]; then
echo "OK!"
fi
# OK!

複数条件を記載する場合は、他の言語のelse、elseif`にあたるものを利用します。

# $xが60より大きかったら、OK!と出力

# $xが60以下で、40より大きければsoso...と出力
# $xが上記以外であればng...と出力
x=20
if [ $x -gt 60 ]; then
echo "OK!"
elif [ $x -gt 40 ]; then
echo "soso..."
else
echo "ng..."
fi
# ng...

動画内で説明はありませんでしたが、UNIXコマンドのwhichは、指定したコマンドのパス(場所)を検索してフルパスで表示します。

今回利用した[which [で検索すると、私の場合は/usr/bin/[と出力されます。

つまり、/usr/bin/以下にこのコマンドがあるということになります。


12 case文で条件分岐

case文は以下のように書きます。

# $xが60より大きかったら、OK!と出力

signal="red"
case $signal in
"red") # redの場合
echo "stop!"
;;
"yellow") # yellowの場合
echo "caution!"
;;
"green") # greenの場合
echo "go!"
;;
*) # その他の場合
echo "..."
;;
esac
# stop!


13 while文でループ処理

while文は以下のように書きます。

# 1から10まで出力

i=0
while [$i -lt 10 ]
do
i='expr $i + 1`
echo $i
done

また、if文と併用してcontinuebreakも使用できます。

whileの後、条件文の場所を:(ヌルコマンド)にすると無限ループが発生します。

そちらをcontinuebreakも利用して下記のようにします。

i=0

while : #無限ループ開始
do
i='expr $i + 1`
# $iが3であればスキップ
if [$i -eq 3 ]; then
continue
fi
# $iが10より大きければループを抜ける
if [$i -gt 10 ]; then
break
fi

echo $i
done


14 for文

例えば、1〜5を順番に出力したいときは以下のように書きます。

for i in 1 2 3 4 5

do
echo $i
done

dodoneによって囲われた部分を繰り返します。

また、条件の部分で、配列を使用できます。

a=(1 2 3 4 5)

for i in ${a[@]} # 配列を全て展開
do
echo $i
done

そもそも条件文をいちいち書くのが面倒な場合は、seqが使えます。

シェルスクリプトで指定回数ループ処理

seqは第1引数から第2引数までの値を出力してくれます。

第3引数を指定すれば、出力間隔も指定できます(デフォルトは1)

for i in `seq 1 100`

do
echo $i
done
# 1〜100まで出力される

また、while文と同様に、continuebreakも使用できます。


15 コマンド引数を渡す

コマンドからファイルに対して引数を渡すとします。

その場合、$0、$1`という特殊な変数を与えることで、引数をプログラムん与えることができます。

以下のファイルを用意します。


hello.sh

# プログラム自身の名前(ファイル名)

echo $0

# 1番目の引数を出力
echo $1

# 2番目の引数を出力
echo $2

# 全ての引数を出力
echo $@

# 全ての引数の数を出力
echo $#


そして、コマンドラインから以下のように入力します。

$ ./hello.sh a b

結果はこちらになると思います。

./hello.sh      # $0

a # $1
b # $2
a b # $@
2 # $#

ちなみに、10以上の引数を使う場合には{}をつけて変数を用意します。

echo ${10}


16 ユーザーからの入力を受け付ける

例えば、ユーザーから特定の文字を入力されないと、延々と次に進めないようなプログラムを作るとします。

while :

do
read key
echo "you pressed $key"
if [ $key = "end" ]; then
break
fi
done

これを実行すると、まず何も起こりませんが、ここで適当なキーを打ち込んでみてください。

例えば、aを打ち込みます。

you pressed a

上のように表示されます。

適当な文字列を打ち込んでも同様な状態になると思います。

ここで、if文で指定したendと押すと、終了します。

readコマンドを利用しましたが、これは入力された文字列を指定された変数に格納する標準コマンドです。

今回はkeyを変数として指定しているため、入力された文字列を出力できます。

readコマンド

また、他にもselectというコマンドもあります。

selectfor文のようなループ系のコマンドですが、用意された配列の中からユーザーに選択してもらい、その文字列を変数に格納します。

select option in var opt home exit

do
echo "you pressed $option"
break
done

これを実行すると、以下のように表示されると思います。

1) var

2) opt
3) home
4) exit
#?

ここで、キーボードで1を押すと、

you pressed var

2を押すと、

you pressed opt

と出力されます。

これにif文などを組み合わせれば、このボタンでこの処理、このボタンでこの処理、とできるようです。

制御構文 FOR / SELECT


17 別ファイルの内容を読み込む

以下のテキストファイルを用意します。

yesterday

today
tomorrow

このファイルを読み込んで、1行ずつ出力させます。

それでは、シェルスクリプトのファイルに以下のように書きます。

# while readで与えられたファイルを1行ずつ読み込む

while read line
do
echo $line
# $1は第1引数のための変数
done <$1

そしてコマンドラインに戻り、以下のように入力します。

$ ./hello.sh data.txt

結果は、

yesterday

today
tomorrow

これは、第1引数で読み込むファイル名を指定して、$1に格納します。

while read$1に入っている、つまりdata.txtの行数まで繰り返し、文字列を出力します。

こちらはよく利用するようで、いろいろなパターンがあるようです。

シェルスクリプトでよく使われる while read line 4パターン


18 関数

関数は以下のように作成します。

functionは省略可能)

function hello() {

echo "hello"
}

# 関数の呼び出し
hello

これで、./hello.shを呼び出すと、helloと出力されます。

例えば、引数を利用したいときは、$1$2などの標準変数を利用して、以下のように書き換えます。

function hello() {

echo "hello $1 and $2"
}

# 関数の呼び出し
hello Mike Tom

関数内で利用する変数ですが、シェルスクリプトでは関数内で宣言しても、関数外で使用できます。

function hello() {

echo "hello $1 and $2"
i=5
echo $i
}

# 関数の呼び出し
hello Mike Tom
echo $i

これは、関数内、関数外両方ともechoで出力されます。

関数内のみで使用したい場合は、変数宣言時にlocalをつけて、local i=5とします。


参考資料