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

Bashスクリプトにヘルプ機能を追加する方法【関数とオプション処理の基礎】

このシリーズの第1回では、わずか1行の小さなBashスクリプトを作成し、シェルスクリプトを作る理由や、コンパイル型プログラムよりもシステム管理者にとって効率的な選択肢である理由について解説しました。第2回では、他のBashプログラムの出発点として使えるシンプルなテンプレートの作成に取り掛かり、そのテスト方法を探りました。

そして今回、4部構成の第3回では、シンプルなヘルプ(Help)関数の作成方法とその使い方を解説します。ヘルプ機能を実装する過程で、関数の使い方や、-h のようなコマンドラインオプションの処理方法も学ぶことができます。

なぜヘルプ機能が必要なのか?

どんなにシンプルなBashプログラムでも、たとえ初歩的なものであっても、何らかのヘルプ機能を持たせるべきです。私が書くBashシェルスクリプトの多くは使用頻度が低いため、必要なコマンドの正確な構文を忘れてしまうことがよくあります。また逆に、複雑すぎて頻繁に使っていても、オプションや引数を確認する必要があるものもあります。

ヘルプ機能が組み込まれていれば、コード本体を読み返すことなく、こうした情報を確認できます。さらに言えば、充実したヘルプ機能はプログラムドキュメントの一部でもあるのです。

関数について

シェル関数とは、Bashのプログラムステートメント(文)のリストをシェル環境に格納したものであり、他のコマンドと同じように、コマンドラインで名前を入力することで実行できます。シェル関数は、使用している他のプログラミング言語によっては「プロシージャ(手続き)」や「サブルーチン」と呼ばれることもあります。

関数は、スクリプト内やコマンドラインインターフェース(CLI)から、他のコマンドと同様に名前を指定して呼び出します。CLIプログラムやスクリプトの中では、関数が呼び出されるとその内部のコマンドが実行され、処理が完了すると呼び出し元に制御が戻り、呼び出し元の次の一連のステートメントが実行されます。

関数の構文は次のとおりです。

FunctionName(){program statements}

実際にCLI上で簡単な関数を作成して試してみましょう(関数は、それを作成したシェルインスタンスの環境に保存されます)。ここでは「hello world」を意味する hw という関数を作ります。以下のコードをCLIに入力して Enter キーを押してください。その後、他のシェルコマンドと同じように hw を入力して実行します。

[student@testvm1 ~]$ hw(){ echo "Hi there kiddo"; }
[student@testvm1 ~]$ hw
Hi there kiddo
[student@testvm1 ~]$

さて、「Hello world」という定番の挨拶には少し飽きたので、別の言葉にしてみました。次に、現在定義されているすべての関数を一覧表示してみましょう。関数は多数あるため、ここでは新しく作成した hw 関数だけを表示しています。関数は、コマンドラインから、あるいはプログラム内で呼び出されると、プログラムされた処理を実行し、終了して制御を呼び出し元(コマンドライン、またはスクリプト内では呼び出しステートメントの直後にある次のBashステートメント)へ返します。

[student@testvm1 ~]$ declare -f | less
<snip>
hw ()
{
    echo "Hi there kiddo"
}
<snip>

この関数はもう不要なので削除しておきましょう。unset コマンドを使えば削除できます。

[student@testvm1 ~]$ unset -f hw ; hw
bash: hw: command not found
[student@testvm1 ~]$

ヘルプ関数の作成

hello プログラムをエディタで開き、著作権表示の後、echo "Hello world!" ステートメントの前に、以下のHelp関数を追加します。このHelp関数は、プログラムの簡単な説明、構文図、利用可能なオプションの短い説明を表示します。動作を確認できるよう、Help関数の呼び出しも追加し、さらにコメント行を加えて、関数部分とメインプログラム部分の視覚的な区切りを明確にしましょう。

################################################################################
# Help                                                                #
################################################################################
Help()
{
   # Display Help
   echo "Add description of the script functions here."
   echo
   echo "Syntax: scriptTemplate [-g|h|v|V]"
   echo "options:"
   echo "g     Print the GPL license notification."
   echo "h     Print this Help."
   echo "v     Verbose mode."
   echo "V     Print software version and exit."
   echo
}

################################################################################
################################################################################
# Main program                                                     #
################################################################################
################################################################################

Help
echo "Hello world!"

このHelp関数に記載しているオプションは、私が普段書くプログラムで典型的なものですが、現時点ではまだコードとして実装されていません。プログラムを実行してテストしてみましょう。

[student@testvm1 ~]$ ./hello
Add description of the script functions here.

Syntax: scriptTemplate [-g|h|v|V]
options:
g     Print the GPL license notification.
h     Print this Help.
v     Verbose mode.
V     Print software version and exit.

Hello world!
[student@testvm1 ~]$

現時点では「必要なときだけヘルプを表示する」ロジックが組み込まれていないため、プログラムは常にヘルプを表示します。関数が正しく動作していることが確認できたら、次はコマンドラインから -h オプションを付けてプログラムを起動したときにのみヘルプを表示するロジックを追加していきます。

オプションの処理

-h のようなコマンドラインオプションを処理できることは、Bashスクリプトに強力な能力をもたらします。プログラムの動作を指示したり、挙動を変化させたりできるからです。-h オプションの場合は、ヘルプテキストを端末セッションに出力した後、プログラムの残りの部分を実行せずに終了させたいわけです。

コマンドラインで入力されたオプションを処理する機能は、while コマンド(while の詳細については『How to program with Bash: Loops』を参照)を getopts コマンドおよび case コマンドと組み合わせて使うことで、Bashスクリプトに追加できます。

getopts コマンドは、コマンドラインで指定されたすべてのオプションを読み取り、そのリストを作成します。以下のコードでは、while コマンドが各オプションに対して変数 $option を設定しながら、オプションのリストを順番に処理していきます。case ステートメントは各オプションを順に評価し、対応するブロック内のステートメントを実行します。while ステートメントは、すべてのオプションが処理されるか、プログラムを終了させる exit ステートメントに到達するまで、オプションリストの評価を続けます。

必ず echo "Hello world!" ステートメントの直前にあったHelp関数の呼び出しを削除してください。メイン部分は次のようになります。

################################################################################
################################################################################
# Main program                                                     #
################################################################################
################################################################################
################################################################################
# Process the input options. Add options as needed.            #
################################################################################
# Get the options
while getopts ":h" option; do
   case $option in
      h) # display Help
         Help
         exit;;
   esac
done

echo "Hello world!"

-h の case ブロックにある exit ステートメントの末尾の「セミコロン2つ(;;)」に注目してください。これは、case ステートメントに追加する各オプションの末尾を示すために必ず必要な記述です。

テスト

これでテストは少し複雑になりました。さまざまなオプションを付けた場合と、オプションなしの場合の両方でプログラムをテストし、それぞれどのように応答するかを確認する必要があります。まずはオプションなしでテストし、「Hello world!」が正しく表示されることを確認しましょう。

[student@testvm1 ~]$ ./hello
Hello world!

うまくいきました。次に、ヘルプテキストを表示するロジックをテストします。

[student@testvm1 ~]$ ./hello -h
Add description of the script functions here.

Syntax: scriptTemplate [-g|h|t|v|V]
options:
g     Print the GPL license notification.
h     Print this Help.
v     Verbose mode.
V     Print software version and exit.

期待どおりに動作していますね。そこで今度は、想定外のオプションを入力したときにどうなるかを試してみましょう。

[student@testvm1 ~]$ ./hello -x
Hello world!
[student@testvm1 ~]$ ./hello -q
Hello world!
[student@testvm1 ~]$ ./hello -lkjsahdf
Add description of the script functions here.

Syntax: scriptTemplate [-g|h|t|v|V]
options:
g     Print the GPL license notification.
h     Print this Help.
v     Verbose mode.
V     Print software version and exit.

[student@testvm1 ~]$

プログラムは、個別の応答が定義されていないオプションについては、エラーを出さずに単純に無視します。しかし最後の例(オプションとして -lkjsahdf を指定したケース)に注目してください。オプションのリストの中に h が含まれているため、プログラムはそれを認識してヘルプテキストを表示しました。このテストにより、現在のプログラムには不正な入力を検知して終了する仕組みがないことが分かりました。

明示的な一致がないオプションに対応するため、case ステートメントにもう1つのブロックを追加できます。この汎用のケースは、個別のマッチを定義していないあらゆるものに一致します。以下のように、全キャッチのマッチである \? を最後のケースとして配置します。追加の個別ケースは、必ずこの最終ケースより前に記述しなければなりません。

while getopts ":h" option; do
   case $option in
      h) # display Help
         Help
         exit;;
     \?) # incorrect option
         echo "Error: Invalid option"
         exit;;
   esac
done

先ほどと同じオプションを使ってプログラムを再度テストし、動作がどう変わったか確認してみてください。

ここまでの成果

この記事では、コマンドラインオプションを処理する機能とヘルプ手続きを追加することで、かなりの前進を果たしました。現時点でのBashスクリプト全体は次のとおりです。

#!/usr/bin/bash
################################################################################
#                               scriptTemplate                   #
#                                                                #
# Use this template as the beginning of a new program. Place a short         #
# description of the script here.                            #
#                                                               #
# Change History                                             #
# 11/11/2019  David Both    Original code. This is a template for creating    #
#                           new Bash shell scripts.                      #
#                           Add new history entries as needed.          #
#                                                              #
#                                                              #
################################################################################
################################################################################
################################################################################
#                                                             #
#  Copyright (C) 2007, 2019 David Both                       #
#  LinuxGeek46@both.org                                     #
#                                                              #
#  This program is free software; you can redistribute it and/or modify    #
#  it under the terms of the GNU General Public License as published by    #
#  the Free Software Foundation; either version 2 of the License, or       #
#  (at your option) any later version.                       #
#                                                              #
#  This program is distributed in the hope that it will be useful,        #
#  but WITHOUT ANY WARRANTY; without even the implied warranty of         #
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          #
#  GNU General Public License for more details.               #
#                                                              #
#  You should have received a copy of the GNU General Public License       #
#  along with this program; if not, write to the Free Software           #
#  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA   #
#                                                             #
################################################################################
################################################################################
################################################################################

################################################################################
# Help                                                              #
################################################################################
Help()
{
   # Display Help
   echo "Add description of the script functions here."
   echo
   echo "Syntax: scriptTemplate [-g|h|t|v|V]"
   echo "options:"
   echo "g     Print the GPL license notification."
   echo "h     Print this Help."
   echo "v     Verbose mode."
   echo "V     Print software version and exit."
   echo
}

################################################################################
################################################################################
# Main program                                                     #
################################################################################
################################################################################
################################################################################
# Process the input options. Add options as needed.            #
################################################################################
# Get the options
while getopts ":h" option; do
   case $option in
      h) # display Help
         Help
         exit;;
     \?) # incorrect option
         echo "Error: Invalid option"
         exit;;
   esac
done

echo "Hello world!"

このバージョンのプログラムは、必ず十分に徹底的にテストしてください。ランダムな入力を与えて、どのような挙動になるか観察しましょう。また、ハイフン(-)を付けずに有効・無効なオプションを渡した場合のテストも行ってみてください。

次回予告

今回は、ヘルプ関数の追加と、コマンドラインオプションを処理して選択的にヘルプを表示する機能を実装しました。プログラムは少しずつ複雑になってきており、完全性を担保するためには、より多くのテストパスが必要になっています。

次回の記事では、変数の初期化とサニティチェック(妥当性検証)を行い、プログラムが適切な条件のもとで確実に実行されるようにする方法を解説します。

参考リソース

  • How to program with Bash: Syntax and tools
  • How to program with Bash: Logical operators and shell expansions
  • How to program with Bash: Loops

本記事シリーズは、David Both氏による3部構成のLinux自習コース『Using and Administering Linux—Zero to SysAdmin』の第2巻 第10章に一部基づいています。

  1. Macにスタートアップ項目を追加する方法|ログイン時にアプリを自動起動させる2つのテクニック

    Macには、日々の作業で使う書類やアプリケーションが数多く入っていることでしょう。その中には、Safariやメールなど、毎日のように頻繁に開く便利なアプリも含まれています。 そんなとき、Macにログインした瞬間にこれらのアプリや書類が自動的に起動してくれたら、とても便利だと思いませんか?この記事では、よく使う項目をMacのスタートアップ項目として登録する方法を詳しく解説します。 一度スタートアップ項目として登録してしまえば、ログインのたびに手動でアプリを開く必要はなくなります。 スタートアップ項目とは? 追加手順を解説する前に、まず「スタートアップ項目」とは何なのかを簡単におさらいしておきまし

  2. PCの不要なプログラムを完全にアンインストールする6つの方法

    整理整頓されたクリーンなPCは、散らかったPCよりも常に快適です。高いパフォーマンスとセキュリティを維持するためには、PCを常に最適な状態に保つことが大切です。不要なプログラムが溜まると動作が重くなり、ハードディスクの容量も圧迫されてしまいます。 そのため、定期的なお掃除は必須ですが、実は簡単な作業ではありません。不要なプログラムをアンインストールしようとしても、痕跡が残ってしまったり、さまざまな理由で削除できないことも少なくありません。 そんなときはどうすればいいのでしょうか?この記事では、不要なプログラムを完全にアンインストールするためのさまざまな方法をご紹介します。 最も一般的で手軽な