最新版は以下に記載しました。
bashスクリプトの書き方
bash変数展開の使い方
bashスクリプトのデバッグ方法
bashdbの使い方
ShellCheckコマンドの使い方
#1 はじめに
bsahのサンプルスクリプトです。
自分が仕事で使いそうなものをまとめました。
#2 環境
VMware Workstation 15 Player上の仮想マシンを使いました。
仮想マシンの版数は以下のとりです。
[root@server ~]# cat /etc/redhat-release
CentOS Linux release 7.6.1810 (Core)
[root@server ~]# uname -r
3.10.0-957.el7.x86_64
[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
スクリプトにaa
とbb
を引数として与えてみます。
[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.sh
をtest.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"
[root@server ~]# ./test.sh
./test.sh
test
test.log
[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にaa
とbb
を渡してみます。
[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
[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
[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で連結する
シェルスクリプトのデバッグ