シェルスクリプト入門 書き方のまとめ
シェルスクリプト入門として, 基本的な書き方をまとめました. 長いですが, 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/sh
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
上のコードを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.