★★★ Twitterやってます。よかったらフォローお願いします ★★★

シェルスクリプト作成メモ(基本編)

IT
スポンサーリンク

シェルスクリプトの作成メモ(基本編)

約15年ぶりぐらいにシェルスクリプトを書きました。若手時代に同じコマンドの塊を数回打ち込んだらすぐににスクリプト化していた私ですが、すっかりWindowsライクな日々を送るうちに、書き方を忘れかけてましたので、完全に忘れる前に備忘録作ります。

書き始め

拡張子は .sh が一般的
$ vi hoge.sh

ちょっと書いてみる。

#!/bin/sh
echo "Hello, World!"
exit 0

実行してみる。

battan@linuxfx-01:~/work$ sh hoge.sh
Hello, World!
battan@linuxfx-01:~/work$

スクリプトのいろいろな実行

実行権限をつける
$ chmod 755 hoge.sh

実行する時はカレントパスを指定して実行
$ ./hoge.sh

実行権限の無いスクリプトを実行する時
$ bash hoge.sh

実行結果をファイルに出力
$ ./hoge.sh > output.txt

実行結果を画面とファイルに同時出力
$ ./hoge.sh | tee -a output.txt

実行結果を表示しない
$ ./hoge.sh > /dev/null 2>&1
chmod
777 : 全員だれでも実行可能、そして編集可能、フルオープン
755 : 全員だれでも実行可能、但し編集できるのは自分だけ
644 : 自分だけ編集可能、他は読み書き可能
640 : 自分は編集可能、同一グループは参照のみ、その他は見れない
600 : 自分だけ編集可能な秘密のファイル

主なファイルのパーミッション。ディレクトリの場合は実行権限付けないと中に入れないので要注意。

スクリプトをデバッグモードで実行

shに-x オプションを付けると、スクリプト実行時のコマンドや表示されるので、デバッグできます。

-x オプションを付けるとデバッグ実行
$ sh -x ./hoge.sh

シェルスクリプト内でデバッグ指定する時は、次のように書きます。

#!/bin/sh -x
echo "Hello, World!"
exit 0

引数

シェルスクリプトには、引数を渡すことができます。

$ ./hoge.sh 引数1 引数2 引数3 ... 引数n 

引数は、シェルスクリプト内では次の文字列で取得できます。

$0 スクリプト名
$1 $2 $3 … n番目の引数
$* 引数すべて
$# 引数の数
#!/bin/sh

echo プログラム名: $0
echo 引数全体: $*
echo 引数の数: $#
echo 引数1: $1
echo 引数2: $2
echo 引数3: $3

exit 0
battan@linuxfx-01:~/work$ ./arg.sh hoge piyo 10 20 30
プログラム名: ./arg.sh
引数全体: hoge piyo 10 20 30
引数の数: 5
引数1: hoge
引数2: piyo
引数3: 10
battan@linuxfx-01:~/work$

変数

変数名=値 で、定義する。

文字列の変数結合は並べればOK。数値として計算する場合は、exprを使う。

#!/bin/sh

# 文字列結合
str1="hello,"
str2="world!"
echo "${str1} ${str2}"

# 数値計算
x=10
y=20
z=`expr $x + $y`
echo "x=${x}"
echo "y=${y}"
echo "z=${z}"

exit 0
battan@linuxfx-01:~/work$ ./var.sh
hello, world!
x=10
y=20
z=30
battan@linuxfx-01:~/work$

終了コード

スクリプトの終了コードを指定できます。一般的には正常終了した場合は、ゼロ(0)、異常終了時はそれ以外のコードを異常の内容が判別できるように指定します。

#!/bin/sh
exit 0

スクリプトを実行した側で終了コードを確認するには、実行直後に echo $? で確認できます。

battan@linuxfx-01:~/work$ ./exitcode.sh
battan@linuxfx-01:~/work$ echo $?
0
battan@linuxfx-01:~/work$
スポンサーリンク

制御構文

処理分岐

IF文

引数の内容で処理分岐する。IF文の終了はIFのスペルを逆から並べてFI。

if [ $1 = "apple" ]; then
    echo "りんご"
elif [ $1 = "lemon" ]; then
    echo "レモン"
else
    echo "その他"
fi
battan@linuxfx-01:~/work$ ./if.sh apple
りんご
battan@linuxfx-01:~/work$ ./if.sh lemon
レモン
battan@linuxfx-01:~/work$ ./if.sh banana
その他
battan@linuxfx-01:~/work$

制御文だから、IF文のカッコの前後のスペース無くてもよいだろう、と思いがちですが、実は [ は式として評価するコマンドですので、前後スペースが無いとLinuxコマンドとして成り立たなくなりエラーになってしまう点が要注意です。
whichコマンドで確認すると、ちゃんといます。

ちなみにtrueとfalseも制御文ではなく、コマンドです。

battan@linuxfx-01:~/work$ which [
/usr/bin/[
battan@linuxfx-01:~/work$

Case文(スイッチ|Switch)

先程のIF文をCase文で書き直してみます。うん、並列分岐が多い場合はやっぱりCaseがいいね。ちなみにCase文を終了もIF文と同様にスペル逆転。

case "$1" in
    "apple")
        echo "りんご";;
    "lemon")
        echo "レモン";;
    *)
        echo "その他";;
esac
battan@linuxfx-01:~/work$ ./case.sh apple
りんご
battan@linuxfx-01:~/work$ ./case.sh lemon
レモン
battan@linuxfx-01:~/work$ ./case.sh banana
その他
battan@linuxfx-01:~/work$

ループ処理

While文

カウンター処理はWhile文が便利です。

cnt=1
while [ ${cnt} -lt 20 ]; do
    echo -n ${cnt},
    cnt=`expr ${cnt} + 1`
done
echo ${cnt}
battan@linuxfx-01:~/work$ ./while.sh
1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20
battan@linuxfx-01:~/work$

echoコマンドに -n オプションを付けると改行無し。

FOR文

予めループするものが決まっている場合は、FOR文ですね。

items="10 30 50"
for i in ${items}; do
    echo $i
done
battan@linuxfx-01:~/work$ ./for.sh
10
30
50
battan@linuxfx-01:~/work$

例えば、今回作ったスクリプトファイルの行をカウントするとかならこんな感じです。

for i in `ls *.sh`; do
    wc -l $i
done

wcコマンドはいろいろ数えてくれる便利コマンドです。-l オプション指定で行数をカウントしてくれます。

battan@linuxfx-01:~/work$ ./linecount.sh
11 arg.sh
13 case.sh
9 for.sh
11 hoge.sh
12 if.sh
8 linecount.sh
17 var.sh
13 while.sh
battan@linuxfx-01:~/work$
制御文で使える演算子

数値の比較は、-gt、-ge、-lt、-gt、-le、-eq、-neを指定します。
演算子で書くと、>、>=、<、<=、=、!= (または <>)です。

文字の比較は、普通に=で一致、!=で不一致。
論理演算は、! (否定)、-a (AND)、-o (OR)。

これ以外でよく使うのは、
-f:ファイルが存在するか否か
-d:ディレクトリが存在するか否か

もっと詳しく知るには、testコマンドのマニュアルを参照してください。

処理実行パターン

一行に続けて処理を書くことができます。

セミコロンで続けて複数の処理を定義できます。また、&&や||で処理結果による後続処理の呼び分けが可能です。

下の例では、cdを実行し、その結果による処理定義です。&&は成功時に実行、||は失敗時に実行です。

cd ~     && echo "cd ~ 処理成功" || echo "cd ~ 処理失敗"
cd /root && echo "cd /root 処理成功" || echo "cd /root 処理失敗"

これを一般ユーザで実行すると、上は成功、下は失敗して、それぞれの後続処理がコールされます。

battan@linuxfx-01:~/work$ ./execute.sh
cd ~ 処理成功
./execute.sh: 4: cd: can't cd to /root
cd /root 処理失敗
battan@linuxfx-01:~/work$

コマンドの実行結果による結果メッセージ表示などの軽微な処理はまとめて書いてしまったほうが可読性が上がります。

スポンサーリンク

関数定義 (function)

引数とローカル変数

シェルスクリプトも処理が複雑になり、コード量が増えると、関数定義しないと訳がわからなくなります。

#!/bin/sh
func () {
    echo 引数全体: $*
    echo 引数の数: $#
    local str="$1 の技術ブログ"
    echo "関数func.str=${str}"
}

str="ばったん"
func ${str} 10 20 30
echo "str=${str}"
exit 0
battan@linuxfx-01:~/work$ ./func_test.sh
引数全体: ばったん 10 20 30
引数の数: 4
関数func.str=ばったん の技術ブログ
str=ばったん

ポイントはスクリプト内で同じstrという変数を使っていますが、関数内はlocal修飾子を付けることでスコープを関数内にできます。付け忘れるとグルーバル変数化してバグの素になるので要注意です。

関数処理結果の受け取り方

先程書いた通り、関数内からグルーバル変数を操作すると関数の意味がなくなってしまうので、関数の処理結果はこのように受け取ります。

#!/bin/sh

func () {
    local str="$1 の技術ブログ"
    echo "${str}"
    return 10
}

str="ばったん"
result=`func ${str}`
exitcode=$?
echo "result=${result}, exitcode=${exitcode}"
exit 0

ポイントは、関数をバッククォート囲みで実行すると、関数内で標準出力したものが呼び出し側の変数にセットできます。

関数の終了コードも直後なら$?で取得できるので別の変数にセットします。

結果、実行するとこのようになります。

battan@linuxfx-01:~/work$ ./func_test2.sh
result=ばったん の技術ブログ, exitcode=10
battan@linuxfx-01:~/work$
ばったん
ばったん

ばったんのオススメ書籍紹介コーナー!

左から基本編2冊、真ん中は応用編、右から数えて2冊目はシェルスクリプトにawkを組み合わせて文字列を切り刻んで自由自在。一番右はオライリー本。

スポンサーリンク

次回は、応用編

基本的によく使う構文のベースはこんな感じです。

次はよく使うパターン編も書きましたので、興味のある方は是非ご覧ください。

ばったん
ばったん

ばったんのオススメ書籍紹介コーナー!

左から入門書、実用的にロジックをすぐに覚えたい方、極めるにはオライリー本です。

シェルスクリプトだけでもかなり強力なスクリプトが組めますが、ここにAWKを組み合わせるとさらに強力なスクリプトが作れます。一番右のAWKを256倍使うための本は特にオススメですので、サーバエンジニアの方は是非一冊どうぞ!

コメント

タイトルとURLをコピーしました