RAKUS Developers Blog | ラクス エンジニアブログ

株式会社ラクスのITエンジニアによる技術ブログです。

【bash入門】bashシェルスクリプトの書き方

こんにちは。インフラエンジニアをしていますknmriiです。
今回はbashシェルスクリプトについて、基本的な書き方やよく使うコマンドなどを紹介していきます。
bashでのプログラミングを学び始めた方や、インフラエンジニアになりたての方のご参考になりましたら幸いです。
また、普段は別の言語に触れられている方についてもサーバサイドではこのようなbashスクリプトが動いていることが多いですので 参考までに一読していただければと思います。

目次

bashシェルスクリプトとは

そもそもbash シェルスクリプトとは何ぞやという話からさせて頂きます。

bashとは、多くのUNIX系OSで標準的に用いられるシェルプログラムの一つ。標準の実行プログラムファイル名(コマンド名)は「bash」だが、「sh」が指定された場合も起動するようシンボリックリンクが置かれることが多い。

IT用語辞典 e-words bash より引用

シェルスクリプトとは、OS(オペレーティングシステム)を操作するためのシェル上で実行できる簡易なプログラミング言語スクリプト言語)。また、そのような言語によって書かれた、複数のOSコマンドや制御文などを組み合わせた簡易なプログラム。一般的にはUNIX系OSのシェルで実行できるものを指す。

IT用語辞典 e-words シェルスクリプト より引用

調べてみたところ上記のような解説がありましたが、入門者にとってはかなり難しく感じると思いますので簡単に言い換えてみるとこのようになります。

bashUnix系(Linux含む)のOSでユーザがコマンドを入力し、それに応じてシステムが処理をしたり応答を返すための仲介をするプログラム(シェル)の一種。
シェルスクリプト → コマンドを組み合わせた簡易的なプログラミング言語

bashを用いたシェルスクリプトの書き方

ここからは実際のbashを用いたシェルスクリプトの書き方を紹介します。

シバン シェバング(Shebang)

シェルスクリプトの1行目によく記載されている以下のようなものをシバン シェバング(Shebang)と呼びます。
実行するシェルスクリプトインタプリタを指定しています。
シバンを以下のように記載することで、シェルスクリプトbashを使用して実行されるようになります。
ついつい忘れがちな1行かもしれませんが、bashにしかない機能を使用したい場合に、この記載がないと意図したように動かない可能性があります。
bashを用いたシェルスクリプトを作成する際には必ず書くようにしましょう。

#!/bin/bash

また、記載するパスを#!/bin/shとしても大抵の場合/bin/sh/bin/bashシンボリックリンクとなっているため、動作はしますが一部挙動が変化するようです。

#!/bin/sh

参考:「分かりそう」で「分からない」でも「分かった」気になれるIT用語辞典 /bin/sh

コメント

bashスクリプト内にコメントを残したい場合には、行の先頭に#を付けましょう。

スクリプト

#!/bin/bash

# echo "bashでこの行はコメントです"
echo "bashでこの行はコメントではありません"

〇実行結果

bashでこの行はコメントではありません

変数の定義

bashでの変数は変数名でつなげることで定義することができます。
一般的には、変数と定義する際に初期値の代入をすることが多いです。
この際に、javaでいうintStringのような型を指定する必要はありません。
ただし、基本的にbashでは変数に代入された値の全てが文字列として扱われます。
また、の前後にスペースを入れるとエラーとなりますのでご注意ください。
変数の内容はecho $変数とすることで出力することができます。

変数に値を代入する

bashでは変数と値をで接続するだけで代入が可能です。

スクリプト

#!/bin/bash

NUM=8
MESSAGE=Hello_bash

echo $NUM
echo $MESSAGE

〇実行結果

8
Hello_bash

変数にスペース等が入った文字列を代入する

bashでは、ダブルクオーテーションで値を囲むとスペースが入った文字列も変数に代入することが可能です。

スクリプト

#!/bin/bash

MESSAGE="Hello bash"

〇実行結果

Hello bash

<注意点>
スペースが入った文字列をダブルクオーテーションで囲まずに代入すると、bashのルール的にスペース以降の文字列が別コマンドとして認識されてしまいます。

変数にコマンドの実行結果を代入する

bashにてコマンドの実行結果を変数に代入したい場面が非常に多いです。
そのような場合は、バッククオートでコマンドを囲みます。

#!/bin/bash

HOSTNAME=`hostname`

echo $HOSTNAME
testhost001

スクリプト実行時の引数を変数に代入する

bashスクリプトを実行する際、引数を渡すことができます。
一般的な引数は、一度変数に格納してスクリプト内で使用することが多いです。
また、引数は${1} ${2}のように、渡した順番を指定して呼び出すことも可能です。

スクリプト

#!/bin/bash

 NUM=${1}
 STRING=${2}

 echo "引数1は${NUM}です"
 echo "引数2は${STRING}です"

〇実行結果

$ sh test.sh 1 bash
引数1は1です
引数2はbashです

配列の定義

bashにて配列の定義をする場合は、変数と同様に配列名配列でつなげます。
この際、配列に関しては(A B C)のようにかっこで囲み値ごとにスペースを入れます。

#!/bin/bash

ARRAY=(0 1 2 3 4 5)

bashで配列の中身を参照するには、以下のようにします。

先頭の内容を参照する

スクリプト

#!/bin/bash

ARRAY=(0 1 2 3 4 5)

echo $ARRAY

〇実行結果

0

任意の配列番号の内容の参照する

スクリプト

#!/bin/bash

ARRAY=(0 1 2 3 4 5)

echo "${ARRAY[1]}"

〇実行結果

1

配列の内容を全て参照する

スクリプト

#!/bin/bash

ARRAY=(0 1 2 3 4 5)

echo "${ARRAY[@]}"

〇実行結果

0 1 2 3 4 5

for文

指定回数を繰り返したい場合

bashでのfor文は以下のようにします。
seqコマンドを使用することで指定の回数を繰り返すことが可能です。

スクリプト

#!/bin/bash

for i in `seq 0 5`
do
  echo $i
done

〇実行結果

0
1
2
3
4
5

配列の要素数だけ繰り返したい場合

配列の中身を順番に取り出したいケースはかなり多いと思います。
そのような場合、bashでは以下のようにするとfor文で全て取り出せます。

スクリプト

#!/bin/bash

ARRAY=(January February March April May June July August September October November December)

for i in ${ARRAY[@]}
do
  echo $i
done

〇実行結果

January
February
March
April
May
June
July
August
September
October
November
December

while文

一般的なwhile文

bashでの一般的なwhile文は以下のように記載できます。
[]で囲まれている部分については、testコマンドが実行されているのですが、testコマンドについてはif文の項目で解説しようと思います。

スクリプト

#!/bin/bash

count=0

while [ $count -lt 5]
do
 count=$((++count))
 echo $count
done

〇実行結果 test.txt

1
2
3
4
5

readコマンドを使用したwhile文

テキストファイルの全ての行に対して処理をしたいことが度々あります。
そのような場合に使用するのが、readコマンドを使用したwhile文です。

〇サンプルファイル

January
February
March
April
May
June
July
August
September
October
November
December

スクリプト

#!/bin/bash

while read line
do
  echo $line
done < test.txt

〇実行結果

January
February
March
April
May
June
July
August
September
October
November
December

if文

bashで特定の条件で処理を分岐させたい場合には、if文を使用します。
bashでのif文の多くはtestコマンドを使用します。
また、testコマンドを使用したif文はこのように記載します。

if [ 条件1 ]; then
  処理1
  ## 条件1が真であれば処理1が実行される
elif [ 条件2 ]; then
  処理2
  ## 条件を複数使用したい場合は elif を使用する
  ## 条件2が真であれば処理2が実行される
else
  処理3
  ## 条件1 2 がどちらでも真でなければ処理3が実行される
fi

上記のように []を使用してその中に条件を記載するのですが、その記載方法はtestコマンドに準じています。
testコマンドにて使用できるパラメータの一例として、以下のようなものがあります。

項目 説明
-e [ -e /tmp/testfile ] testfileが存在していれば真
-f [ -e /tmp/testfile ] testfile存在していて通常ファイルであれば真
-d [ -d /tmp/testdir] testdirが存在していてディレクトリであれば真
-z [ -z 文字列 ] 文字列の長さが0であれば真
-n [ -n 文字列 ] 文字列の長さが0でなければ真
-eq [ 数値1 -eq 数値2 ] 数値1と数値2が等しければ真(=)
-ne [ 数値1 -ne 数値2 ] 数値1と数値2が等しければ真(!=)
-gt [ 数値1 -gt 数値2 ] 数値1が数値2より大きければ真(>)
-ge [ 数値1 -ge 数値2 ] 数値1が数値2以上であれば真(>=)
-lt [ 数値1 -lt 数値2 ] 数値1が数値2より小さければ真(<)
-le [ 数値1 -le 数値2 ] 数値1が数値2以下であれば真(<=)
= [ 文字列1 = 文字列2 ] 文字列1と文字列2が等しければ真
!= [ 文字列1 != 文字列2 ] 文字列1と文字列2が等しくなければ真

if文の使用例としてのサンプルは以下です。

スクリプト

#!/bin/bash

NUMS=(1 2 3 4 5 6 7 8 9 10)
STRINGS=(January February March April May June July August September October November December)

echo "===========TEST1==========="
for num in ${NUMS[@]}
do
  if [ $num -gt 5 ]; then
    echo "$numは5より大きいです。"
  else  
    echo "$numは5以下です。"
  fi
done
echo "===========TEST2==========="
for num in ${NUMS[@]}
do
  if [ $num -le 5 ]; then
    echo "$numは5以下です。"
  else  
    echo "$numは5より大きいです。"
  fi
done
echo "===========TEST3==========="
for str in ${STRINGS[@]}
do
  if [ $str = "January" ];then
    echo "$str は1月です"
    touch /tmp/jaunary.txt
  else
    echo "$str は1月ではありません。"
  fi
done
echo "==========================="

〇実行結果

===========TEST1===========
1は5以下です。
2は5以下です。
3は5以下です。
4は5以下です。
5は5以下です。
6は5より大きいです。
7は5より大きいです。
8は5より大きいです。
9は5より大きいです。
10は5より大きいです。
===========TEST2===========
1は5以下です。
2は5以下です。
3は5以下です。
4は5以下です。
5は5以下です。
6は5より大きいです。
7は5より大きいです。
8は5より大きいです。
9は5より大きいです。
10は5より大きいです。
===========TEST3===========
January は1月です
February は1月ではありません。
March は1月ではありません。
April は1月ではありません。
May は1月ではありません。
June は1月ではありません。
July は1月ではありません。
August は1月ではありません。
September は1月ではありません。
October は1月ではありません。
November は1月ではありません。
December は1月ではありません。
===========================

case文

bashにて特定の条件で処理を分岐させたい場合には、if文ではなくcase文を使用することもできます。 条件分岐が多い場合はこちらの方が便利ですね。

case 値 in
  値1 )
    処理1
    ;;
  値2 )
    処理2
    ;;
  値3 )
    処理3
    ;;
  * )
    処理4 # 処理1~3のどれとも一致しなかった場合は処理4が実行される
esac

使用例としては以下のような感じです。

スクリプト

#!/bin/bash

STRINGS=(January February March April May June July August September October November December)
for str in ${STRINGS[@]}
do
  case $str in
    January )
      echo "$str は1月です"
      ;;
    February )
      echo "$str は2月です"
      ;;
    March )
      echo "$str は3月です"
      ;;
    * )
      echo "$str は1~3月ではありません"
  esac
done

〇実行結果

January は1月です
February は2月です
March は3月です
April は1~3月ではありません
May は1~3月ではありません
June は1~3月ではありません
July は1~3月ではありません
August は1~3月ではありません
September は1~3月ではありません
October は1~3月ではありません
November は1~3月ではありません
December は1~3月ではありません

終わりに

今回はbashによるシェルスクリプトの基本的な書き方や、構文についてを紹介しました。
ほかの言語を学習してからbashを使おうとしたり、始めてプログラミングに触れる方にとって戸惑うことも多いかと思いますが、少しでもご参考になれば幸いです。


◆TECH PLAY
techplay.jp

◆connpass
rakus.connpass.com

Copyright © RAKUS Co., Ltd. All rights reserved.