Bashプログラミング
 Computer >> コンピューター >  >> プログラミング >> Bashプログラミング

シェルスクリプトの関数を理解して書いてみよう – Part VI

関数はあらゆるプログラミング言語において重要な役割を担っています。多くの本格的なプログラミング言語と同様に、bashにも関数が用意されていますが、その実装は限定的なものとなっています。

シェルスクリプトの関数を理解して書いてみよう – Part VI

関数とは何か?

プログラミングにおいて、関数とは特定のタスクを実行するために名前を付けられたプログラムの一部です。この意味で、関数は一種の手続き(procedure)やルーチン(routine)と言えます。関数が呼び出されると、プログラムは現在のコード位置から離れ、関数内の最初の行から実行を開始します。同じコードを繰り返し書く必要がある場合や、同じタスクが何度も発生する場合には、関数の利用を検討しましょう。

たとえば、あるプログラムの中で何度も「ある数値の階乗」を計算する必要がある場面を考えてみてください。階乗を計算するコード全体を毎回書き直す代わりに、その計算部分を一度だけブロックとして記述しておけば、複数の場所で再利用できます。

なぜ関数を書くのか?

  1. コードを再利用できる。
  2. プログラムの可読性が向上する。
  3. プログラム内の変数を効率的に使える。
  4. プログラムを部分的にテストできる。
  5. プログラム全体を一連のサブステップとして把握しやすくなる。

シェルスクリプトにおける関数

シェルスクリプトで関数を記述する一般的な構文は、次のような形式があります。

function func_name {
	...
	commands
	...
}

または

func_name ( ) {
	...
	commands
	...
}

2番目の形式では、開き波括弧を次の行に置くことも可能です。

func_name ( )
{
	...
	commands
	...
}

これらの関数ブロック内には、通常のシェルスクリプトと同様に、有効なコマンドを自由に記述できます。それでは、簡単な関数を含む小さなスクリプトを実際に書いてみましょう。

#!/bin/bash

call_echo ( ) {
	echo 'This is inside function'
}

op=$1

if [ $# -ne 1 ]; then
	echo "Usage: $0 <1/0>"
else
	if [ $1 = 0 ] ; then
		echo 'This is outside function'
	elif [ $1 = 1 ] ; then
		call_echo
	else
		echo 'Invalid argument'
	fi
fi

exit 0

関数の定義は、必ず最初の呼び出しより前に行わなければなりません。他の言語にあるような「事前に宣言だけしておく」という仕組みは存在しない点に注意してください。また、関数の中に関数をネストすることも常に可能です。

注意:空の関数を定義すると、必ず構文エラーになります。

同じ名前の関数が複数回定義された場合、最後に定義されたバージョンが呼び出されます。以下の例で確認してみましょう。

#!/bin/bash

func_same ( ) {
	echo 'First definition'
}

func_same ( ) {
	echo 'Second definition'
}

func_same

exit 0

引数を受け取り、戻り値を返す関数

さらに踏み込んで、引数を受け取り値を返す関数について考えてみます。関数から値を返すには、シェルの組み込みコマンドである return を使用します。構文は以下の通りです。

func_name ( ) {
	...
	commands
	...
	return $ret_val
}

同様に、関数に引数を渡す場合は、スペースで区切って指定します。

func_name $arg_1 $arg_2 $arg_3

関数内部では、引数に $1$2$3 のように順番にアクセスできます。理解を深めるために、関数を使って2つの整数の最大値を求めるサンプルスクリプトを見てみましょう。

#!/bin/bash

USG_ERR=7

max_two ( ) {
	if [ "$1" -eq "$2" ] ; then
		echo 'Equal'
		exit 0
	elif [ "$1" -gt "$2" ] ; then
		echo $1
	else
		echo $2
	fi
}

err_str ( ) {
	echo "Usage: $0 <number1>  <number2>"
	exit $USG_ERR
}

NUM_1=$1
NUM_2=$2

if [ $# -ne 2 ] ; then
	err_str
elif [ `expr $NUM_1 : '[0-9]*'` -eq ${#NUM_1} ] ; then
	if [ `expr $NUM_2 : '[0-9]*'` -eq ${#NUM_2} ] ; then
		max_two $NUM_1 $NUM_2
	else
		err_str
	fi
else
	err_str
fi

exit 0

少し複雑に見えますが、順を追って読めばシンプルです。まずネストされた if-else 文で入力の検証を行います。具体的には、正規表現を使って引数の個数や型(数値であるか)をチェックしています。その後、2つのコマンドライン引数を関数に渡して呼び出し、結果をその場で表示します。これは、関数から大きな整数を return で返すことができないためです。

この問題を回避するもう一つの方法は、グローバル変数を使って関数内で結果を保持することです。次のスクリプトは、その手法を示したものです。

#!/bin/bash

USG_ERR=7
ret_val=

max_two ( ) {
	if [ "$1" -eq "$2" ] ; then
		echo 'Equal'
		exit 0
	elif [ "$1" -gt "$2" ] ; then
		ret_val=$1
	else
		ret_val=$2
	fi
}

err_str ( ) {
	echo "Usage: $0 <number1>  <number2>"
	exit $USG_ERR
}

NUM_1=$1
NUM_2=$2

if [ $# -ne 2 ] ; then
	err_str
elif [ `expr $NUM_1 : '[0-9]*'` -eq ${#NUM_1} ] ; then
	if [ `expr $NUM_2 : '[0-9]*'` -eq ${#NUM_2} ] ; then
		max_two $NUM_1 $NUM_2
		echo $ret_val
	else
		err_str
	fi
else
	err_str
fi

exit 0

最後に、これまでのシェルスクリプトシリーズで解説した課題に、関数を使って挑戦してみましょう。

  1. Linuxシェルスクリプティングの基礎を学ぶ – Part I
  2. 初心者向けシェルプログラミング学習用スクリプト5選 – Part II
  3. Linux BASHスクリプティングの世界を旅する – Part III
  4. Linuxシェルプログラミングの数学的側面 – Part IV
  5. シェルスクリプトでの数式計算 – Part V

次回のパートでは、ローカル変数の使い方や再帰処理など、関数に関するさらなる機能について詳しく解説する予定です。ぜひコメント欄で最新情報をお見逃しなく。

  1. Linuxでカスタムシェル関数とライブラリを作成・活用する方法

    Linuxにおいて、シェルスクリプトはシステム管理タスクの実行や自動化、簡単なコマンドラインツールの作成など、さまざまな場面で役立ちます。本記事では、Linux初心者の方に向けて、独自のシェルスクリプトを保存すべき信頼性の高い場所、カスタムシェル関数やライブラリの書き方、そして他のスクリプトからライブラリ内の関数を呼び出す方法について解説します。 シェルスクリプトの保存場所 スクリプトを実行するときに毎回フルパス(絶対パス)を入力せずに済むようにするには、そのスクリプトを $PATH 環境変数に含まれるいずれかのディレクトリに保存しておく必要があります。 現在の $PATH の内容は、以下のコ

  2. Bashシェル関数入門:実用的な6つのサンプル例で学ぶチュートリアル

    Bashシェル関数とは、複数のUNIX/Linuxコマンドをひとつの名前でグループ化し、後からまとめて実行できるようにする仕組みです。Bashシェル関数は通常のUnixコマンドと同じように呼び出すことができ、現在のシェルコンテキスト内で実行されるため、解釈用の新しいプロセスを生成する必要がありません。bashのエイリアス(alias)と関数はどちらも、長いコマンドや複雑なコマンドに対するショートカットを定義するのに役立ちます。しかし、エイリアスでは制御フローや引数の扱いなど、関数ならではの高度な処理を行うことはできません。本記事では、その違いも含めて詳しく解説していきます。この記事は、進行中の