シェルスクリプト入門 書き方のまとめ

シェルスクリプト入門として, 基本的な書き方をまとめました. 長いですが, 1ページにまとめてみました. 良かったら目次も参考にしてご覧になって下さい.

目次

シェルスクリプトとは

シェルスクリプトとは, シェルの動作をまとめて記述したスクリプトのことです. 決められた文法にしたがって処理を記述することによって, シェルでの処理をまとめて行ったり, 作業を自動化できたりします(例 複数ファイルの一括リネーム). このページでは, 簡単なシェルスクリプトを書いて実行することから初めて, 構文や変数などシェルスクリプトを書くためのルールを解説します.

作り方, 実行の仕方

まず, 簡単なシェルスクリプトの作り方から. 次のようなhello.shというファイルを作ってみましょう. #!/bin/shについては細かいことは気にせずつけておきましょう.


#!/bin/sh
echo "Hello World!"
            

動かしてみると, 以下のようになります.


$ ./hello.sh
bash: ./hello.sh: Permission denied
            

これは, hello.shというファイルを実行する権限が無いことが原因でおこっているので, chmodを使って, 権限をつけてから, 実行します.


$ chmod u+x hello.sh
$ ./hello.sh
Hello World!
            

作り方, 実行の仕方は, 以上になります.

コメント

コメントは, #を使います. #の後ろに書かれた文字はコメントとされ, 読まれません. #は一行のみコメント化されます.

複数行コメントしたい場合は, << + 任意の文字列を使います.

下の例では, 全てのecho "Hello world!"がコメント化されています.


#!/bin/sh
#echo "Hello World!"

<<COMMENT
echo "Hello world!"
echo "Hello world!"
echo "Hello world!"
COMMENT
            

ユーザーからのキーボード入力を受け付ける

readを使えば, ユーザーからのキーボード入力を受け付けることができます. 使い方は以下のようになります.


read -p "Please input your name: " name
echo "Hi, $name."
            

上のコードをinput.shとして保存し, 実行します.


$ ./input.sh
Please input your name: Taro
Hi, Taro.
            

変数

通常の変数

変数は, 文字列や数字などを格納することができます. ちなみに, 変数に代入するときに, 余計なスペースを入れてはいけないので注意してください.

以下のようにして, 変数の内容を呼ぶことができます.


#!/bin/sh

string="Hello world!"

echo string
echo 'string'
echo "string"
echo
echo $string
echo '$string'
echo "$string"
echo
echo ${string}
echo '${string}'
echo "${string}"
echo

num=10

echo num
echo 'num'
echo "num"
echo
echo $num
echo '$num'
echo "$num"
echo
echo ${num}
echo '${num}'
echo "${num}"
echo

name=Taro
age=20

echo '$name is $age years old.'
echo "$name is $age years old."
echo
printf '%s is %d years old.\n' $name $age
printf "%s is %d years old.\n" $name $age
            

上のコードをvar.shとして保存し, 実行します.


$ ./var.sh
string
string
string

Hello world!
$string
Hello world!

Hello world!
${string}
Hello world!

num
num
num

10
$num
10

10
${num}
10

$name is $age years old.
Taro is 20 years old.

Taro is 20 years old.
Taro is 20 years old.
            

基本的に, 変数の内容を使いたいときは, $の後に, 変数名を書くことで使うことができます. また, "(ダブルクォート)で囲むときは変数が展開され, '(シングルクォート)で囲むときは変数が展開されません. ちなみに, 引数なしのechoは, 改行のために入れています.

特別な変数

変数 内容
$0 スクリプトの名前
$1, $2, ..., $9 第1引数, 第2引数, ..., 第9引数
$# 与えられた引数の数
$* 与えられたすべての引数. 引数全体が"で囲まれている
$@ 与えられたすべての引数. 引数一つ一つが"で囲まれている
$- シェルに与えられたフラグ
$? 最後に行ったコマンドの戻り値
$$ 現在のシェルのプロセス番号
$! 最後にバックグラウンドで行ったコマンドのプロセス番号

#!/bin/sh

echo $0
echo $1
echo $3
echo $#
echo $*
echo $@
            

上のコードをsp_var.shとして保存し, 実行します.


$ ./sp_var.sh apple orange banana grape
./sp_var.sh
apple
banana
4
apple orange banana grape
apple orange banana grape
            

演算子

数値計算演算子

書き方は, 計算したい式を, $((と, ))で囲みます.


#!/bin/sh

x=5
y=10
z=$(( x + y ))

echo "x = $x"
echo "y = $y"
echo "x + y = $x + $y = $z"
            

上のコードをadd.shとして保存し, 実行します.


$ ./add.sh
x = 5
y = 10
x + y = 5 + 10 = 15
            
演算子 内容 結果
+ 加法 echo $(( 2 + 3 )) 5
- 減法 echo $(( 2 - 3 )) -1
/ 除法 echo $(( 6 / 2 )) 3
* 乗法 echo $(( 2 * 3 )) 6
% 余り echo $(( 3 % 2 )) 1
x++ インクリメント x=2
echo $(( x++ ))
echo $(( x++ ))
2
3
++x インクリメント x=2
echo $(( ++x ))
echo $(( ++x ))
3
4
x-- デクリメント x=2
echo $(( x-- ))
echo $(( x-- ))
2
1
--x デクリメント x=2
echo $(( --x ))
echo $(( --x ))
1
0
** べき乗 echo $(( 2 ** 3 )) 8

比較演算子

演算子 結果
> echo $(( 2 > 3 )) 0
== echo $(( 2 == 3 )) 0
!= echo $(( 2 != 3 )) 1
< echo $(( 2 < 3 )) 1

これは, 以下で扱う条件文には使えません.

&&||の使い方は, 次の条件文のところでも例示します.

演算子 書き方 内容
; command1;command2 別々のコマンドを連続して書くことができます. コマンドは連続して実行されます pwd ; ls
& command & コマンドをバックグラウンドで実行します sleep 3 & → バックグラウンドでスリープ処理が行われるので, その場ではスリープしません. 処理後にもとのシェルに通知されます
&& command1 && command2 command1が実行され, 戻り値が0であれば, command2を実行する pwd && ls → pwdは実行され, 戻り値0を返すので, pwdのあとlsが実行される
|| command1 || command2 command1が実行され, 戻り値が0であれば, command2を実行する pwd || ls → lsは実行されない
| command1 | command2 command1の標準出力をcommand2に渡す cat file名 | grep

条件文に使える比較演算子

数値を比較する場合は,

演算子 書き方 意味
eq x -eq y x = y [ 5 -eq 5 ] && echo "True" → True
ge x -ge y x >= y [ 5 -ge 3 ] && echo "True" → True
gt x -gt y x > y [ 5 -gt 3 ] && echo "True" → True
le x -le y x <= y [ 3 -le 5 ] && echo "True" → True
lt x -lt y x < y [ 3 -lt 5 ] && echo "True" → True
ne x -ne y x != y [ 1 -ne 2 ] && echo "True" → True

文字列を比較する場合は,

演算子 書き方 意味
= x = y xとyが全く同じ文字列 [ "apple" = "apple" ] && echo "True" → True
!= x != y xとyが同じ文字列でない [ "apple" = "orange" ] && echo "True" → True
z -z x 文字列xの長さが0である [ -z "" ] && echo "True" → True

ファイル, ディレクトリに関しては, 下の表のようなチェックができます. 表の例は次のようなディレクトリ構成の場所で実行しました(ディレクトリ内にfileというファイルと, dirというディレクトリのみ).


$ ls -l
total 0
drwxr-xr-x  2 username  staff  68  4  5 18:00 dir
-rw-r--r--  1 username  staff   0  4  5 17:46 file
            
演算子 書き方 意味
f -f file fileがファイルである [ -f file ] && echo "True" → True
d -d dir dirがディレクトリである [ -d dir ] && echo "True" → True
e -e file(or directory) file(or directory)が存在する [ -e file ] && echo "True" → True
r -r file(or directory) file(or directory)が読み込み可能である [ -r file ] && echo "True" → True
w -w file(or directory) file(or directory)が書き込み可能である [ -w file ] && echo "True" → True
x -x file(or directory) file(or directory)が実行可能である [ -x file ] || echo "True" → True

条件文

条件文の形式

条件文は, ifや, whileなどと合わせて使うか, &&, ||などと合わせて使います. (もしかしたら他に使い道があるかもしれませんが, よく知りません.)

条件文の書き方は, 以下のようになります. 2行目は, 1行目の否定文です.

ちなみに, 条件文には, 先ほどの比較演算子は使えません. 使える演算子は, 後ほど書きます.


test 条件式
test ! 条件式
            

または,


[ 条件式 ]
[ ! 条件式 ]
            

&&, || を使った例

条件文は, &&||を使って以下のように使うことができます.


test 条件式 && 命令(条件式が真の時に実行される)
test 条件式 || 命令(条件式が偽の時に実行される)
test 条件式 && 命令(条件式が真の時に実行される) || 命令(条件式が偽の時に実行される)

echo

[ 条件式 ] && 命令(条件式が真の時に実行される)
[ 条件式 ] || 命令(条件式が偽の時に実行される)
[ 条件式 ] && 命令(条件式が真の時に実行される) || 命令(条件式が偽の時に実行される)
            

上の形を使って例を書くと, 次のようになります.


#!/bin/sh
test 3 -gt 2 && echo "1"
test 3 -lt 2 && echo "2"
test 3 -gt 2 || echo "3"
test 3 -lt 2 || echo "4"
test 3 -gt 2 && echo "5" || echo "6"
test 3 -lt 2 && echo "7" || echo "8"

echo

[ 3 -gt 2 ] && echo "9"
[ 3 -lt 2 ] && echo "10"
[ 3 -gt 2 ] || echo "11"
[ 3 -lt 2 ] || echo "12"
[ 3 -gt 2 ] && echo "13" || echo "14"
[ 3 -lt 2 ] && echo "15" || echo "16"
            

上のコードをcondition.shとして保存し, 実行します.


$ ./condition.sh
1
4
5
8

9
12
13
16
            

制御構文(分岐)

if文

基本形は(条件文に関しては「条件文」を参照して下さい),


if 条件文
then
    命令1
    命令2
    ...
    命令3
fi
            

また, 以下のようにもできます.


if 条件文1
then
    命令1
elif 条件文2
    命令2
elif 条件文3
    命令3
else
    命令4
fi
            

上の条件文の書き方と合わせて例を書くと, 以下のようになります.


#!/bin/sh
if test 3 -gt 2
then
	echo "1"
fi

if [ 3 -gt 2 ]
then
	echo "2"
fi

if test 3 -lt 2
then
	echo "3"
fi

if [ 3 -lt 2 ]
then
	echo "4"
fi
            

上のコードをif.shとして保存し, 実行します.


$ ./if.sh
1
2
            

case文

case文の基本形は, 次のようになります.


case $変数 in
    パターン1)
        命令1
        ;;
    パターン2)
        命令2
        ;;
    パターン3|パターン4)
        命令3
        ;;
    *)
        命令4
        ;;
esac
            

以下に例をあげます.


#!/bin/sh

fruit="apple"
case $fruit in
	apple)
		echo "これはりんごです."
		;;
	orange)
		echo "これはみかんです."
		;;
	babana|grape)
		echo "これはバナナかぶどうです."
		;;
	*)
		echo "これはりんごでもみかんでもバナナでもぶどうでもありません."
		;;
esac
            

上のコードをcase.shとして保存し, 実行します.


$ ./case.sh
これはりんごです.
            

制御構文(ループ)

for文

forループの基本形は, 以下のようになります.


for 変数 in アイテム1 アイテム2 ... アイテムN
do
    命令1
    命令2
    命令3
done
            

例をあげます.


#!/bin/sh

for fruit in apple orange banana
do
	echo $fruit
done
            

上のコードをfor1.shとして保存し, 実行します.


$ ./for1.sh
apple
orange
banana
            

ループの対象が数字のときは, 以下のように書くこともできます.


#!/bin/sh

for i in {1..10}
do
	echo " $i 回目のループです."
done

echo

for (( i = 0 ; i < 10 ; i++ ))
do
	echo " $i 回目のループです."
done
            

上のコードをfor2.shとして保存し, 実行します.


$ ./for2.sh
 1 回目のループです.
 2 回目のループです.
 3 回目のループです.
 4 回目のループです.
 5 回目のループです.
 6 回目のループです.
 7 回目のループです.
 8 回目のループです.
 9 回目のループです.
 10 回目のループです.

 0 回目のループです.
 1 回目のループです.
 2 回目のループです.
 3 回目のループです.
 4 回目のループです.
 5 回目のループです.
 6 回目のループです.
 7 回目のループです.
 8 回目のループです.
 9 回目のループです.
            

コマンドの標準出力を使うこともできます. コマンドを使うときは, $()か, ``で囲みます.


#!/bin/sh

for file in $(ls)
do
	echo $file
done

echo

for file in `ls`
do
	echo $file
done
            

上のコードをfor3.shとして保存し, 実行します.


$ ./for3.sh
file1
file2
file3
file4
file5
for1.sh
for2.sh
for3.sh

file1
file2
file3
file4
file5
for1.sh
for2.sh
for3.sh
            

while文

whileループの基本形は(条件文に関しては「条件文」を参照して下さい), 次のようになります.


while 条件文
do
  命令1
  命令2
  命令3
done
            

ファイルを1行1行読み込むときは,


while IFS= read -r line
do
  命令1
  命令2
  命令3
done < "ファイルのパス"
            

とすればokです.

上に挙げた形の2つの例を示します.


#!/bin/sh

n=1
while [ $n -le 5 ]
do
	echo " $n 回目のループです."
	n=$(( n+1 ))
done
            

上のコードをwhile1.shとして保存し, 実行します.


$ ./while1.sh
 1 回目のループです.
 2 回目のループです.
 3 回目のループです.
 4 回目のループです.
 5 回目のループです.
            

#!/bin/sh

while IFS= read -r line
do
	echo $line
done < "./while1.sh"
            

上のコードをwhile2.shとして保存し, 実行します.


$ ./while2.sh

n=1
while [ $n -le 5 ]
do
echo " $n 回目のループです."
n=$(( n+1 ))
done
            

ファイルを読む際に, セパレーターを使って文章を分けたいときは, 以下のようにすることで実現できます. まず, 以下のファイルを読むことにします.


No.1: apple
No.2: orange
No.3: banana
            

上の3行をfile1とします.

ファイル読み込みの基本形は,


while IFS='セパレーター' read -r field1 field2
do
    命令1
    命令2
    命令3
done < "ファイルのパス"
            

例えば, file1の空白で区切られた2列を逆の位置に変えたいとすると, 以下のようにすることで実現できます.


#!/bin/sh

while IFS=' ' read -r field1 field2
do
	echo $field2 $field1
done < "./file1"
            

上のコードをwhile3.shとして保存し, 実行します.


$ ./while3.sh
apple No.1:
orange No.2:
banana No.3:
            

until文

until文の形はwhile文に似ています. 意味はwhileとは逆で, whileは, 条件が成立している時に命令が実行されますが, untilは, 条件が不成立な時のみ命令を実行します(条件文に関しては「条件文」を参照して下さい).


until 条件文
do
  命令1
  命令2
  命令3
done
            

#!/bin/sh

n=1
until [ $n -gt 5 ]
do
	echo " $n 回目のループです."
	n=$(( n+1 ))
done
            

上のコードをuntil.shとして保存し, 実行します.


$ ./until.sh
 1 回目のループです.
 2 回目のループです.
 3 回目のループです.
 4 回目のループです.
 5 回目のループです.
            

select文

select文を使うと, 指定した複数の要素から, 一つの要素を対話的に選択させることができます. ただ, breakを使わない限りはループが止まらないので, しっかりbreak文を入れましょう. もし無限ループに入ってしまったら, Ctrl+Cなどでぬけだします.

まず, 書き方は,


PS3="ここに書かれた内容が, 実行時に表示されます."
select 変数 in アイテム1 アイテム2 アイテム3
do
  命令1
  命令2
  命令3
done
            

例を見て行きましょう.


#!/bin/sh

PS3="番号で選択して下さい: "
select fruit in apple orange banana grape exit
do
	case $fruit in
		apple|orange|banana|grape)
			echo "$fruit が選択されました!"
			;;
		exit)
			break
			;;
		*)
			echo "1から4の番号で選んで下さい."
			;;
	esac
done
            

上のコードをselect.shとして保存し, 実行します.


$ ./select.sh
1) apple
2) orange
3) banana
4) grape
5) exit
番号で選択して下さい: 1
apple が選択されました!
番号で選択して下さい: 2
orange が選択されました!
番号で選択して下さい: 3
banana が選択されました!
番号で選択して下さい: 4
grape が選択されました!
番号で選択して下さい: 5
            

文字列処理

置換

変数に入った文字列を置換するには, 以下のようにします.

構文 内容
${var/old/new} 変数var内の最初のoldをnewに置換する
${var//old/new} 変数var内の全てのoldをnewに置換する

#!/bin/bash

for i in apple-apple-apple apple-apple-orange apple-apple-grape
do
	echo ${i/apple/banana}
done

echo

for i in apple-apple-apple apple-apple-orange apple-apple-grape
do
	echo ${i//apple/banana}
done
            

[訂正] 文字列置換は, /bin/shでは行えなかったかもしれません. 上記コード内で一行目を#!/bin/shとしていたため, 訂正致します. 正しくは, 一行目を#!/bin/bashとしてください.

上のコードをstring1.shとして保存し, 実行します.


$ ./string1.sh
banana-apple-apple
banana-apple-orange
banana-apple-grape

banana-banana-banana
banana-banana-orange
banana-banana-grape
            

削除

変数に入った文字列を削除するには, 以下のようにします.

構文 内容
${var#pattern} 変数var内の最初の文字から検索し, patternにマッチした最短の文字列を削除する
${var##pattern} 変数var内の最初の文字から検索し, patternにマッチした最長の文字列を削除する
${var%pattern} 変数var内の最後の文字から検索し, patternにマッチした最短の文字列を削除する
${var%%pattern} 変数var内の最後の文字から検索し, patternにマッチした最長の文字列を削除する

#!/bin/sh

str=apple-apple-apple

echo ${str#*-}
echo ${str##*-}
echo ${str%-*}
echo ${str%%-*}
            

上のコードをstring2.shとして保存し, 実行します.


$ ./string2.sh
apple-apple
apple
apple-apple
apple
            

複数行のテキストの出力(ヒアドキュメント)

複数行の出力をしたい時には, ヒアドキュメントを使うと便利です. まず, ヒアドキュメントを使わずに複数行のテキストを出力しようとすると, 次のようにして実現できます.


#!/bin/sh

echo apple
echo orange
echo banana
            

上のコードをoutput_text.shとして保存し, 実行します.


$ ./output_text.sh
apple
orange
banana
            

複数行にわたる出力を行うことが出来ましたが, ヒアドキュメントを使うことでもっと容易に実現できます. 使い方は, まずテキストの開始となる文字列を決めます(下の例では, EOT(end of textの略)としていますが, 別の文字列でも良いです). その後は, 出力したいテキストを適当に書きます. テキストを書き終わったら, 最初に決めた文字列のみの行を入力します.


#!/bin/sh

cat << EOT
apple
orange
banana
EOT
            

上のコードをheredoc1.shとして保存し, 実行します.


$ ./heredoc1.sh
apple
orange
banana
            

このテキストをリダイレクト(出力, 追加)するには, 次のようにします.


#!/bin/sh

cat << EOT > fruits.txt
apple
orange
banana
EOT

cat << EOT >> fruits.txt
grape
strawberry
EOT
            

上のコードをheredoc2.shとして保存し, 実行します.


$ ./heredoc2.sh
$ cat fruits.txt
apple
orange
banana
grape
strawberry
            

関数

関数の形式

関数は, 1行で書くときには, 命令の最後に;(セミコロン)を付けなければいけません. 下に1行で書いた関数と, 複数行で書いた関数の例を挙げます. また, 例の通り, 関数の引数には, $1, $2,...を使用します.


#!/bin/sh

hello() { echo "Hello $1."; }

goodbye()
{
	echo "Goodbye $1."
	echo "Goodbye $2."
}

hello Taro
goodbye Taro Jiro
            

上のコードをfunction.shとして保存し, 実行します.


$ ./function.sh
Hello Taro.
Goodbye Taro.
Goodbye Jiro.