一般的なプログラム言語でいう関数と同様、シェルスクリプトでも関数を定義する事が可能です。シェル関数は古くから組み込まれた、非常によく使われる機能です。ある名称で関数として定義し、一連のコマンドを処理させるだけのことです。
コマンド実行や変数定義などが可能なため、複数回同じ処理を行う場合は、関数に置き換え簡潔なコーディングとするのが良いでしょう。
関数を定義・使用するときの書式について
関数を定義する際は( )付きで関数名を記述し、中かっこ"{" で関数の始まりを示し、その次の行から処理内容をそれぞれ書いていきます。
処理が終わったら関数の終わりを示す中かっこ"}"の行を記述して終わりです。なお、関数名の前にfunctionをつける事もありますが、これは省略が可能です。
また、この関数を同じスクリプト内や関数を定義したファイルを読み込んだ別スクリプトで呼び出すときには( )は不要です。
関数の書式
関数名 ( ) {
実行処理
}
関数名のあとの丸括弧は関数の定義として必要なものです。通常その中には何も書きません。
下記は、簡単な関数の記述例です。
test() {
echo "hello"
uname -a
}
次の様に、一行で記述することも可能です。
$ test() { echo "hello"; uname -a; }
シェルスクリプトにおいて、「;(セミコロン)」はコマンドの区切りを意味します。したがって、改行コードではなく「;(セミコロン)」を使うことによって、複数のコマンドを1行に記述することが出来ます。
実際にコンソール上で動作するか試してみます。
1 2 3 4 |
[root@CentOS7 ~]# test() { echo "hello"; uname -a;} [root@CentOS7 ~]# test hello Linux CentOS7.localdomain 3.10.0-957.el7.x86_64 #1 SMP Thu Nov 8 23:39:32 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux |
コンソール上へ、文字列"hello"とOS情報が出力されました。
関数における引数やパラメータについて
関数においてもシェルスクリプトと同様に引数を用いる事が可能です。
また、$1/$2/$3.../や、$#/$@/$*など、スクリプトで言う定位置パラメータと呼ばれる変数も、関数で一部を除いてほぼ同様に使用する事が出来ます。
ただし、シェルスクリプトの場合は、「$0」に関しては、スクリプトのパスが格納されますが、関数の場合は関数名ではなく、呼び出し元のスクリプトと同じになります。
関数名を取得したい場合は、変数 ${FUNCNAME[0]}を使いましょう。
$ test() {
echo "hello"
echo $1
echo $2
echo $0
echo ${FUNCNAME[0]}
}
実際にコンソール上で実行してみます。
ここでは「no1」と「no2」の2つの引数を付与して実行します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
[root@CentOS7 ~]# test() { > echo "hello" > echo $1 > echo $2 > echo $0 > echo ${FUNCNAME[0]} > } ----------------------------------------------------------- [root@CentOS7 ~]# test no1 no2 hello no1 no2 -bash 👈 「$0」の結果 ※1 test 👈 「${FUNCNAME[0]}」の結果 ※2 |
※1 コマンドラインの親シェル(スクリプト) は-bashなので-bashと表示されます。また、スクリプトの場合はスクリプト名が返ります。
※2 「${FUNCNAME[0]}」には、関数名が返ります。
戻り値について
シェルスクリプトは、実行終了時にステータスコード(リターンコード)を返却します。シェルスクリプトでは、各コマンドの終了ステータスが「0」なら「真(true)」、「0以外」なら「偽(false)」として扱われます。
関数ではその関数の戻り値を自分で設定することができます。「return」コマンドを任意の位置に書き、そこで戻り値を設定できます。
return [n]
[n]と書いている部分に任意の番号を設定すれば、それがこの関数の戻り値となります。
ここで返す値は通常のコマンドの実行終了ステータスと同じ取り扱いになり、シェル変数の「$?」で参照可能です。
明示的にreturnコマンドを書いていない場合には、「関数内の最後に実行されたコマンドの実行終了ステータス」になります。
実際に戻り値を取得する関数を記述して、試してみます。
# 実行関数作成
$ test() {
echo "value"
return 255
}
# 関数を実行して、標準出力を変数へ格納
$ rtn=$(test)
# 変数の中身を出力
$ echo ${rtn}
# 戻り値を取得して出力
$ test; echo $?
コンソール上で実行してみます。
1 2 3 4 5 6 7 8 9 10 11 |
[root@CentOS7 ~]# test() { > echo "value" > return 255 > } ----------------------------------------------------------- [root@CentOS7 ~]# rtn=$(test) [root@CentOS7 ~]# echo ${rtn} value [root@CentOS7 ~]# test; echo $? value 255 |
1-4行目:関数「test()」をコンソール上で作成しています。
6行目:関数「test()」をコンソール上で実行しています。
※ 関数からの標準出力が変数「${rtn}」にセットされるため、コンソール上への直接出力は行われません。
7行目:変数「${rtn}」に格納されている出力結果をコンソール上へ出力させています。
※ 8行目で出力されている「value」が出力結果です。
ここで「echo $?」を実行しても、「echo」コマンド自身の出力に成功している為、リターンコードは「0」で返却されます。
9行目:再度、関数「test()」を実行して、その戻り値を取得して表示しています。
※ 上記のコマンドでは、戻り値へ255を設定しているので、関数の戻り値は「255」が返されています。
ココに注意
戻り値(リターンコード)とは、コマンドの成否を表す終了ステータス(数値)が特殊変数 $? に自動で設定される値を指します。
コマンド成功時には「0」失敗時には「1」(コマンドやエラーの種類によっては 0 以外)が「標準出力」とは別に返却されます。戻り値と標準出力は全く別のものです。
変数の範囲について
多くのプログラミング言語では、関数内で宣言された変数はデフォルトでその関数内でしか使用できないローカル変数となります。
グローバル変数
シェルスクリプトでは、関数内で宣言(初使用)された変数は、デフォルトで呼び出し元スクリプト自体や内部の他の関数でも利用可能なグローバル変数として振る舞います。
ここがほかのプログラミング言語などとは大きく異なる点です。
では、実際に試してみましょう。
① 一度、コンソール上のプロンプトへ下記コマンドにて関数「glb_func」を作成します。
② その後、作成した関数「glb_func」を呼び出し、動作を確認します。ここまでは、普通の関数と同じですね。
③ こんどは、作成した関数「glb_func」内部の変数「$glb_test」を、外部から直接呼び出してみます。
実際に打ち込むコードは、下記になります。
# 関数作成
$ glb_func() {
glb_test="hoge"
echo ${glb_test}
}
# 関数呼び出し
$ glb_func
# 直接関数内の変数「glb_test」を参照
$ echo ${glb_test}
では、実際に実行してみましょう。
1 2 3 4 5 6 7 8 9 10 |
[root@CentOS7 ~]# glb_func() { > glb_test="hoge" > echo ${glb_test} > } ----------------------------------------------------------- [root@CentOS7 ~]# glb_func hoge [root@CentOS7 ~]# echo ${glb_test} 👈 外部から変数「$glb_test」を呼び出している hoge [root@CentOS7 ~]# |
外部からの変数「$glb_test」の呼び出しに、応答しているのが分かります。
変数「$glb_test」の値は、関数外でも値を保持されている為、関数終了後でもコマンドラインからの変数「$glb_test」呼び出しに応答します。
グローバル変数としたくない場合は、変数の前にlocalを指定すると、関数外では値が保持されないローカル変数として扱う事が可能です。
ローカル変数
グローバル変数とは逆に、外部から参照できない変数として定義する場合には、ローカル変数を使用します。任意の変数の直前に「local」を付与します。
先ほどのシェル関数内の変数を、ローカル変数へ書き直してみましょう。ただし、混乱を避けるため関数名は「lcl_func」と変更しています。
# 関数作成(local)
$ lcl_func() {
local lcl_test="hoge"
echo ${lcl_test}
}
# 関数呼び出し
$ lcl_func
# 直接関数内の変数「lcl_test」を参照
$ echo ${lcl_test}
1 2 3 4 5 6 7 8 9 |
[root@CentOS7 ~]# lcl_func() { > local lcl_test="hoge" > echo ${lcl_test} > } ----------------------------------------------------------- [root@CentOS7 ~]# lcl_func hoge [root@CentOS7 ~]# echo ${lcl_test} 👈 外部から変数「$lcl_test」への参照が出来ません。 |
ローカル変数が、外部から参照できないことが分かります。
複数ファイルに分割して記述
冒頭でも触れましたが、関数を定義したファイル「comFunc.shrc(共通関数定義ファイル)」を外部ファイルとして作成しておき、「.(ドット)」コマンドまたは「source」コマンドで実行シェルスクリプトに読み込むことで、「comFunc.shrc」内に定義された関数を外部から使用することが可能です。
ちょっと難しそうですが、ファイルの分割はシェルスクリプトを作成していく過程で、非常に重要な概念です。
テンプレートファイルを使って、実際に試してみましょう。まず、下記の2つのシェルスクリプトを作成します。
- comFunc.shrc(共通関数定義ファイル)
※ 本記事では、共通関数定義ファイルの拡張子に「resource」を表す「comFunc.shrc」を末尾に付与していますが、特に成約などはありません。 - func.sh(実行シェルスクリプト)
共通関数定義ファイルの分割
[ <BASE_DIR>/scripts/com/comFunc.shrc ](共通関数定義ファイル)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
#!/bin/sh #_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/ # # ver.1.0.0 yyyy.mm.dd # # Usage: # $ <BASE_DIR>/scripts/com/comFunc.sh # # Description: # 共通関数ファイル # # 設計書 # なし # #_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/ # ---------------------------------------------------------- # variables (変数の宣言領域) # ---------------------------------------------------------- rc=2 # ---------------------------------------------------------- # functions (関数を記述する領域) # ---------------------------------------------------------- # -------------------------------------------------- # devider. # -------------------------------------------------- # return N/A # -------------------------------------------------- line (){ echo -e "\\n ------------" echo -e " ▼ ${1}" } # -------------------------------------------------- # test function # -------------------------------------------------- # return N/A # -------------------------------------------------- glb_func() { 👈 グローバル関数 glb_test="これは共通関数ファイルの変数「glb_test」の値です。" 👈 グローバル変数 echo ${glb_test} } |
実行シェルスクリプトの作成
[ <BASE_DIR>/scripts/bin/func.sh ] (実行シェルスクリプト)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 |
#!/bin/sh #_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/ # # ver.1.0.0 yyyy.mm.dd # # Usage: # $ sh <BASE_DIR>/scripts/bin/func.sh # # Description: # テスト関数実行スクリプト # # 設計書 # なし # #_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/ . `dirname $0`/../com/comFunc.shrc 👈 共通関数定義ファイルを読み込む # ---------------------------------------------------------- # variables (変数の宣言領域) # ---------------------------------------------------------- scope="var" rc=2 # ---------------------------------------------------------- # functions (関数を記述する領域) # ---------------------------------------------------------- scope="func" # -------------------------------------------------- # devider. # -------------------------------------------------- # return N/A # -------------------------------------------------- line (){ echo -e "\\n ------------" echo -e " ▼ ${1}" } # -------------------------------------------------- # test function # -------------------------------------------------- # return N/A # -------------------------------------------------- # ---------------------------------------------------------- # pre-process (事前処理ロジックを記述する領域) # ---------------------------------------------------------- scope="pre" # ---------------------------------------------------------- # main-process (メインロジックを記述する領域) # ---------------------------------------------------------- scope="main" glb_func 👈 共通関数ファイルから「glb_func」関数を呼び出し if [ $? -eq 0 ]; then rc=0 fi # ---------------------------------------------------------- # post-process (事後処理ロジックを記述する領域) # ---------------------------------------------------------- scope="post" exit $rc |
実行結果を下記に示します。
1 2 3 |
[root@CentOS7 bin]# sh func.sh これは共通関数ファイルの変数「glb_test」の値です。 [root@CentOS7 bin]# |
実行シェルスクリプト「func.sh」内で、共通関数ファイル内の「glb_func」を呼び出し、変数「${glb_test}」の値ががターミナル上へ出力されました。
以上のことからもわかる通り、実行シェルスクリプトを作成する場合は、予め共通で使用するであろう関数群を共通関数定義ファイル(javaでいうところの共通クラス)として外部へ作成しておき、実行シェルスクリプトは、都度、必要に応じて共通関数定義ファイルを参照するように作成します。
Shellスクリプト基礎知識(全11記事+1)
├─シェルスクリプトの基本事項!
├─変数と特殊変数について!
├─演算子「算術演算子」「比較演算子」について!
├─条件分岐「if」「case」について!
├─ループ処理「for」「while」について!
├─文字列置換「bash」「sed」について!
├─複数行のテキスト出力!ヒアドキュメントについて!
├─書式?戻り値?シェルスクリプト内の関数について!
├─シェルの組み込みコマンドについて!
├─クォートとは?コマンド置換とは?実現方法と内容の違いについて!
└─リダイレクトとは?標準入力・出力、標準エラー出力等について!
(補足)シェルスクリプトの設計書とは?必要な項目や書き方等を解説!