LoginSignup
4
1

シェルスクリプトの書き方

Last updated at Posted at 2020-09-22

最新版は以下に記載しました。
bashスクリプトの書き方
bash変数展開の使い方
bashスクリプトのデバッグ方法
bashdbの使い方
ShellCheckコマンドの使い方

#1 はじめに
bsahのサンプルスクリプトです。
自分が仕事で使いそうなものをまとめました。

#2 環境
VMware Workstation 15 Player上の仮想マシンを使いました。
仮想マシンの版数は以下のとりです。

CentOSの版数
[root@server ~]# cat /etc/redhat-release
CentOS Linux release 7.6.1810 (Core)
カーネル版数
[root@server ~]# uname -r
3.10.0-957.el7.x86_64
bash版数
[root@server ~]# bash --version
GNU bash, バージョン 4.2.46(2)-release (x86_64-redhat-linux-gnu)
-snip-

#3 Hello World

スクリプト
[root@server ~]# cat test.sh
#!/usr/bin/bash
echo "Hello World"
実行権付与
[root@server ~]# chmod 744 test.sh
スクリプト実行
[root@server ~]# ./test.sh
Hello World

#4 引数の使い方
引数の意味は、次のとおり。
$0:スクリプトの名前そのものを表す。
$1,$2,$3...:引数を表す。

サンプルスクリプト
[root@server ~]# cat test.sh
#!/usr/bin/bash
echo "$0" "$1" "$2"

スクリプトにaaを引数として与えてみます。

実行結果
[root@server ~]# ./test.sh aa
./test.sh aa

スクリプトにaabbを引数として与えてみます。

実行結果
[root@server ~]# ./test.sh aa bb
./test.sh aa bb

#5 if文の使い方

サンプルスクリプト
[root@server ~]# cat test.sh
#!/usr/bin/bash

if [ "$1" -eq 1 ]; then
  echo 'one'
elif [ "$1" -eq 2 ]; then
  echo 'two'
else
  echo 'other'
fi
実行結果
[root@server ~]# ./test.sh 1
one
[root@server ~]# ./test.sh 2
two
[root@server ~]# ./test.sh 3
other

#6 for文の使い方

##6.1 リストを使った繰り返し

構文
for 変数 in リスト
do
  処理
done
サンプルスクリプト
[root@server ~]# cat test.sh
#!/usr/bin/bash
for i in 1 2 3
do
   echo $i
done
実行結果
[root@server ~]# ./test.sh
1
2
3

##6.2 スクリプトに渡される引数を全て使う方法
シェルの特殊変数$@はスクリプトに渡される引数すべてを意味します。

サンプルスクリプト
[root@server ~]# cat test.sh
#!/usr/bin/bash
for x in "$@"
do
  echo "$x"
done
実行結果
[root@server ~]# ./test.sh 11
11
[root@server ~]# ./test.sh 11 22
11
22

#7 ファイル名の扱い方
##7.1 スクリプトの名前を表示する方法
$0は、スクリプ自身の名前を表します。

サンプルスクリプト
[root@server ~]# cat test.sh
#!/usr/bin/bash
echo "$0"
実行結果
[root@server ~]# ./test.sh
./test.sh

##7.2 パス名からファイル名を取り出す方法
ここでは、パス名からファイル名を取り出したあと
basenameコマンドを使って、ファイルの拡張子を削除してみます。

サンプルスクリプト
[root@server ~]# cat test.sh
#!/usr/bin/bash
echo "$0"
tmp=$(basename "$0")
echo "$tmp"

tmp=$(basename "$0" .sh)
echo "$tmp"
実行結果
[root@server ~]# ./test.sh
./test.sh
test.sh
test

##7.3 スクリプトの拡張子を変更する方法
スクリプトの拡張子を変更してみます。
ここでは、test.shtest.logに変更してみます。

サンプルスクリプト
[root@server ~]# vi test.sh
[root@server ~]# cat test.sh
#!/usr/bin/bash

echo "$0"
TMP=$(basename "$0" .sh)
echo "$TMP"

LOG_FILE=$TMP.log
echo "$LOG_FILE"
実行結果(その1)
[root@server ~]# ./test.sh
./test.sh
test
test.log
実行結果(その2)
[root@server ~]# cp test.sh aa.sh
[root@server ~]# ./aa.sh
./aa.sh
aa
aa.log

#8 ファイルのインクルード方法(source)
sourceコマンドを使って、functionsファイルを読み込んでみます。

サンプルスクリプト(本体)
[root@server ~]# cat test.sh
#!/usr/bin/bash
source ./functions

add 1 2
echo "Ans:"$?
サンプルスクリプト(共通定義)
[root@server ~]# cat functions
#!/usr/bin/bash
function add() {
 a=`expr "$1" + "$2"`
 return "$a"
}
実行結果
[root@server ~]# ./test.sh
Ans:3

#9 ファイル、ディレクトリの存在有無を調べる方法
##9.1 ファイルの存在有無を調べる方法

サンプルスクリプト
[root@server ~]# cat test.sh
#!/usr/bin/bash
echo "$1"

if [ -f "$1" ]; then
  echo 'exist'
else
  echo 'not exist'
fi
実行結果(ファイルが存在する場合)
[root@server ~]# touch /tmp/hoge
[root@server ~]# ./test.sh /tmp/hoge
/tmp/hoge
exist
実行結果(ファイルが存在しない場合)
[root@server ~]# rm -f /tmp/hoge
[root@server ~]# ./test.sh /tmp/hoge
/tmp/hoge
not exist

##9.2 ディレクトリの存在有無を調べる方法

サンプルスクリプト
[root@server ~]# cat test.sh
#!/usr/bin/bash
echo "$1"

if [ -d "$1" ]; then
  echo 'exist'
else
  echo 'not exist'
fi
実行結果(ディレクトリが存在する場合)
[root@server ~]# mkdir /tmp/hoge
[root@server ~]# ./test.sh /tmp/hoge
/tmp/hoge
exist
実行結果(ディレクトリが存在しない場合)
[root@server ~]# rm -fr /tmp/hoge
[root@server ~]# ./test.sh /tmp/hoge
/tmp/hoge
not exist

##9.3 AND条件を複数指定する方法(-a)
事前準備として、テスト用のファイルを作成します。

テスト用ファイル作成
[root@server ~]# touch /tmp/test1.txt
[root@server ~]# touch /tmp/test2.txt
[root@server ~]# touch /tmp/test3.txt

作成した3つのファイル全てが存在する場合はtrue
1つでも存在しないとfalseを表示するスクリプトを作成します。

サンプルスクリプト
[root@server ~]# cat tp.sh
#!/usr/bin/bash

if [ -f /tmp/test1.txt -a \
     -f /tmp/test2.txt -a \
     -f /tmp/test3.txt ]; then
  echo "true"
else
  echo "false"
fi
実行結果
[root@server ~]# ./tp.sh
true

次に、テスト用に作成したファイルを1つ削除してみます。

ファイルの削除
[root@server ~]# rm /tmp/test3.txt
rm: 通常の空ファイル `/tmp/test3.txt' を削除しますか? y

test1.txt,test2.txt,test3.txtの3つが存在しないので、
実行結果が、falseになったことがわかります。

実行結果
[root@server ~]# ./tp.sh
false

##9.4 OR条件を複数指定する方法(-o)
事前準備として、テスト用のファイルを作成します。

テスト用ファイル作成
[root@server ~]# touch /tmp/test1.txt
[root@server ~]# touch /tmp/test2.txt
[root@server ~]# touch /tmp/test3.txt

作成した3つのファイルのいずれかが存在する場合はtrue
全て存在しないとfalseを表示するスクリプトを作成します。

サンプルスクリプト
[root@server ~]# cat tp.sh
#!/usr/bin/bash

if [ -f /tmp/test1.txt -o \
     -f /tmp/test2.txt -o \
     -f /tmp/test3.txt ]; then
  echo "true"
else
  echo "false"
fi

テスト用に作成したファイルを1つ削除してみます。

ファイルの削除
[root@server ~]# rm /tmp/test3.txt
rm: 通常の空ファイル `/tmp/test3.txt' を削除しますか? y

test1.txt,test2.txtが存在するので、実行結果がtrueであることがわかります。

実行結果
[root@server ~]# ./tp.sh
true

残りのファイルも全て削除してみます。

ファイルの削除
[root@server ~]# rm /tmp/test1.txt
rm: 通常の空ファイル `/tmp/test1.txt' を削除しますか? y
[root@server ~]# rm /tmp/test2.txt
rm: 通常の空ファイル `/tmp/test2.txt' を削除しますか? y```

作成した3つのファイル全てが存在しないので、スクリプトの実行結果はfalseになります。

実行結果
[root@server ~]# ./tp.sh
false

#10 関数の使い方
##10.1 関数の呼び出し方法
sourceコマンドは、ファイル(functions)をインクルードするコマンドです。
メイン関数から、関数sub1を呼び出してみます。

サンプルスクリプト(関数の呼び出し側)
[root@server ~]# cat test.sh
#!/usr/bin/bash

source ./functions
sub1
サンプルスクリプト(関数本体)
[root@server ~]# cat functions
#!/usr/bin/bash

function sub1() {
  echo "sub1"
}
実行結果
[root@server ~]# ./test.sh
sub1

##10.2 引数の使い方
メイン関数から関数sub1に11を渡します。
そして、関数sub1から関数sbu2にaabbを渡してみます。

サンプルスクリプト(関数の呼び出し側)
[root@server ~]# cat test.sh
#!/usr/bin/bash

source ./functions
sub1 11
サンプルスクリプト(関数本体)
[root@server ~]# cat functions
#!/usr/bin/bash

function sub1() {
  echo "$1"
  sub2 "aa" "bb"
}

function sub2() {
  echo "$1" "$2"
}
実行結果
[root@server ~]# ./test.sh
11
aa bb

##10.3 戻り値の使い方(数値編)

サンプルスクリプト(関数の呼び出し側)
[root@server ~]# cat test.sh
#!/usr/bin/bash

source ./functions
sub1 11
echo $?

関数への引数から1を引いて、その結果を戻り値として返します。

サンプルスクリプト(関数本体)
[root@server ~]# cat functions
#!/usr/bin/bash

function sub1() {
  tmp=$(expr "$1" - 10)
  return "$tmp"
}
実行結果
[root@server ~]# ./test.sh
1

##10.4 戻り値の使い方(文字列編)

サンプルスクリプト(関数の呼び出し側)
[root@server ~]# cat test.sh
#!/usr/bin/bash

source ./functions
sub1 aa
サンプルスクリプト(関数本体)
[root@server ~]# cat functions
#!/usr/bin/bash

function sub1() {
  tmp=Sample-$1
  echo "$tmp"
}
実行結果
[root@server ~]# ./test.sh
Sample-aa

#11 標準出力、標準エラー出力をファイルにリダイレクトする方法
aaというファイルを作成します。bbは作成しません。

ファイルの確認
[root@server ~]# touch aa
[root@server ~]# ls aa bb
ls: bb にアクセスできません: そのようなファイルやディレクトリはありません
aa

lsコマンドを実行して、標準出力と標準エラー出力をlogファイルに保存します。

[root@server ~]# ls aa bb > log 2>&1
[root@server ~]# cat log
ls: bb にアクセスできません: そのようなファイルやディレクトリはありません
aa

#12 配列の使い方

サンプルスクリプト
[root@server ~]# cat tp.sh
#!/usr/bin/bash

package_list1=("glibc" "iproute")
package_list2=("httpd")

function searche_package() {
    name=$1[@]
    a=("${!name}")
    for package_name in "${a[@]}" ; do
        rpm -qa | grep ^"${package_name}"
    done
}

if [ "$1" -eq 1 ]; then
  searche_package package_list1
elif [ "$1" -eq 2 ]; then
  searche_package package_list2
fi
実行結果(その1)
[root@server ~]# ./tp.sh 1
glibc-headers-2.17-260.el7.x86_64
glibc-debuginfo-common-2.17-260.el7.x86_64
glibc-devel-2.17-260.el7.x86_64
glibc-debuginfo-2.17-260.el7.x86_64
glibc-common-2.17-260.el7.x86_64
glibc-2.17-260.el7.x86_64
iproute-4.11.0-14.el7.x86_64
実行結果(その2)
[root@server ~]# ./tp.sh 2
httpd-tools-2.4.6-93.el7.centos.x86_64
httpd-2.4.6-93.el7.centos.x86_64

#13 デバッグ方法

##13.1 トレースオプションを指定する方法(-x)
1行目に-xオプションを指定して、スクリプトを実行しみます。

サンプルスクリプト
[root@server ~]# cat test.sh
#!/usr/bin/bash -x
echo "$1"

if [ -d "$1" ]; then
  echo 'exist'
else
  echo 'not exist'
fi

実行したコマンドと実行結果が表示されていることがわかります。

実行結果
[root@server ~]# ./test.sh /tmp
+ echo /tmp
/tmp
+ '[' -d /tmp ']'
+ echo exist
exist

##13.2 ソースコードも一緒に出力する方法(-v)
1行目に-vオプションも指定して、スクリプトを実行しみます。

サンプルスクリプト
[root@server ~]# cat test.sh
#!/usr/bin/bash -vx
echo "$1"

if [ -d "$1" ]; then
  echo 'exist'
else
  echo 'not exist'
fi
実行結果
[root@server ~]# ./test.sh /tmp
#!/usr/bin/bash -vx
echo "$1"
+ echo /tmp
/tmp

if [ -d "$1" ]; then
  echo 'exist'
else
  echo 'not exist'
fi
+ '[' -d /tmp ']'
+ echo exist
exist

##13.3 スクリプトを変更せずにデバッグする方法(-x)
13.1の方法ではデバッグ対象のスクリプトを修正する必要があります。
ここでは、スクリプトを修正せずにデバッグしてみます。

実行結果
[root@server ~]# bash -x test.sh /tmp
+ echo /tmp
/tmp
+ '[' -d /tmp ']'
+ echo exist
exist

#Z 参考情報
逆引きシェルスクリプト

shellcheckコマンドの使い方
シェルのif文でAND条件を複数指定したい
bashで条件を複数ORで連結する
シェルスクリプトのデバッグ

4
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
1