1. 概要

シェルプログラミングでは、関数を作成するとき、通常、それらをシェルスクリプトに入れます。 シェルスクリプトの関数には、再利用できるコマンドのグループが含まれています。

このチュートリアルでは、シェルスクリプトファイルの外部からこのような関数を呼び出す方法を学習します。

2. シェルスクリプトファイルの例

スクリプトの外部から関数を呼び出す方法について説明する前に、例を通して問題を理解してみましょう。

シェルスクリプトファイルmyScript.shを作成しましょう。

$ cat myScript.sh
#!/bin/bash
#A variable
VAR="VAR inside the script"
# print INFO message to stdout
# Argument: 
#    $1: INFO message to print
log_info() {
    local MSG="$1"
    printf "%s - [INFO] %s\n" "$(date)" "$MSG"
}

# print ERROR message to stderr
# Argument: 
#    $1: ERROR message to print
log_error() {
    local MSG="$1"
    printf "%s - [ERROR] %s\n" "$(date)" "$MSG" >&2
}

myScript.sh ファイルでは、変数 VAR と、 log_info()および log_error()の2つの関数を定義しました。 スクリプトファイル内で関数を呼び出すことができます。

次に、ヒアドキュメント機能を使用して、 myScript.sh に2行を追加して、2つの関数を呼び出してから、スクリプトを実行します。

$ cat <<EOF>> myScript.sh 
heredoc> log_info "This is an INFO message."
heredoc> log_error "This is an ERROR message."
heredoc> EOF

$ ./myScript.sh 
Mon 24 Aug 2020 10:27:09 PM CEST - [INFO] This is an INFO message.
Mon 24 Aug 2020 10:27:09 PM CEST - [ERROR] This is an ERROR message.

そして、ご覧のとおり、2つの関数が呼び出されます—これまでのところ、非常に優れています。

それでは、myScript.sh。の外部から関数を呼び出す方法を見てみましょう。

3. スクリプトのソース

スクリプトファイルの調達は、スクリプトファイルで定義された関数を呼び出す最も簡単な方法です。 

sourceコマンドと関数呼び出しを 「&&」演算子ワンライナーを構築するには:

$ . myScript.sh && log_info "I am an INFO message outside the script file"
Mon 24 Aug 2020 10:47:34 PM CEST - [INFO] I am an INFO message outside the script file

例が示すように、スクリプトファイルを取得した後、コマンドラインから関数を呼び出すことができます。

4. スクリプトを調達する際の問題

スクリプトファイルを調達してその中の関数を呼び出すのは非常に簡単です。 ただし、状況によっては、スクリプトファイルを直接入手できない場合があります。 次に、調達に問題があるいくつかのケースを見てみましょう。

4.1. 変数が上書きされる

スクリプトを入手した後、変数の名前が同じである場合は、現在のシェルに設定されている変数の値を上書きします

$ VAR="A very important message to print"; . myScript.sh && log_info "$VAR"
Mon 24 Aug 2020 11:05:16 PM CEST - [INFO] VAR inside the script

上記の例が示すように、スクリプトを入手した後、変数VARの値がスクリプトの値で上書きされています。 これは、スクリプトを入手すると、スクリプトが現在のシェルで実行されるためです。 したがって、期待される情報ログを取得できません。

4.2. スクリプト内のすべてのコマンドが実行されます

スクリプトを調達することのもう1つの副作用は、スクリプト内のすべてのコマンドを実行することです。 myScript.shにいくつかのコマンドを追加しましょう。

$ cat myScript.sh
#!/bin/bash
VAR="VAR inside the script"
log_info() ...
log_error() ...
# simulate some command to send emails to all users
echo "Sending emails to all email addresses in users.txt..."
echo "Done"
# log
log_info "Emails have been sent to all users."

新しく追加されたコマンドは、すべてのユーザーへの電子メールの送信をシミュレートします。

ファイルを入手してlog_info()関数を呼び出すとどうなるか見てみましょう。

$ . myScript.sh && log_info "An INFO message outside the script"
Sending emails to all email addresses in users.txt...
Done
Mon 24 Aug 2020 11:26:48 PM CEST - [INFO] Emails have been sent to all users.
Mon 24 Aug 2020 11:26:48 PM CEST - [INFO] An INFO message outside the script

出力が示すように、期待されるログ出力が出力されています。 ただし、電子メールを送信するためのコマンドも実行されています。 スクリプトでlog_info()関数を呼び出してログメッセージを書き込むだけの場合は、このアクションですべてのユーザーに電子メールを送信したくないことは明らかです。

スクリプトを調達せずに、スクリプトで必要な関数を呼び出す方法を見つけることができれば、より良いでしょう。

5. スクリプトを使用せずに関数を呼び出す

通常、シェルスクリプトに関数が含まれている場合、関数宣言は、 myScript.sh で電子メールを送信するためのコマンドなど、具体的な作業を行うコマンドよりも優先されます。

公開する関数とスクリプトに含まれる他のコマンドの間に関数呼び出し処理コードを少し追加することで、スクリプトを変更できます。

5.1. スクリプトを拡張して外部関数呼び出しを処理する

例は、このアプローチをより簡単に理解するのに役立ちます。 まず、変更されたスクリプトファイルと、それが外部関数呼び出しをどのように処理するかを見てみましょう。

$ cat myScript.sh 
#!/bin/bash
VAR="VAR inside the script"
log_info() ...
log_error() ...

case "$1" in
    "") ;;
    log_info) "$@"; exit;;
    log_error) "$@"; exit;;
    *) log_error "Unkown function: $1()"; exit 2;;
esac

# simulate to send emails to all users
....

関数宣言とメール送信コマンドの間にcaseステートメントを追加しました。 case ステートメントは、外部からの関数呼び出しを管理する役割を果たします。

これにより、コマンド ./ myScript.sh function_name arguments を実行して、スクリプト全体を調達することなく、myScript.shで定義された関数を呼び出すことができます。

$ ./myScript.sh log_info "An INFO message outside the script"
Tue 25 Aug 2020 02:01:08 PM CEST - [INFO] An INFO message outside the script

例が示すように、予想されるログが表示されます。 また、メールを送信するためのコマンドは実行されません。

さらに、スクリプトを入手しないため、現在のシェルの変数はスクリプトによって上書きされません。

$ VAR="An ERROR message outside the script"; ./myScript.sh log_error "$VAR"
Tue 25 Aug 2020 02:04:25 PM CEST - [ERROR] An ERROR message outside the script

さらに、存在しない関数を呼び出そうとすると、次のエラーメッセージが表示されます。

$ ./myScript.sh log_warn "A WARN message outside the script"
Tue 25 Aug 2020 02:07:42 PM CEST - [ERROR] Unkown function: log_warn()

最後に、 case ステートメントは、スクリプトの通常の実行に影響を与えません。

$ ./myScript.sh 
Sending emails to all email addresses in users.txt...
Done
Tue 25 Aug 2020 02:13:54 PM CEST - [INFO] Emails have been sent to all users.

5.2. 使い方

ここで、caseステートメントが外部関数呼び出しを処理する方法を理解しましょう。

コマンド./myScript.sh function_name引数を使用して関数を呼び出すと、 function_nameがスクリプトの最初の引数になります。 したがって、 case ステートメントで“ $ 1”変数を確認できます。

  • “”);; $ 1 引数が空の場合、外部関数呼び出しではなく、スクリプトの通常の実行です。 したがって、caseステートメントを超えて実行を続行します
  • log_info)“ $ @”; exit ;; –関数名が一致する場合、一致した関数をすべての引数で呼び出し、関数の実行後に実行を終了します
  • *)log_error“不明な関数:$ 1()”; exit 2 ;; –一致する関数名が見つからない場合、呼び出し元が無効な関数を呼び出そうとしたと考えられます。 したがって、エラーログを出力して終了します

6. 共通の機能を別のスクリプトに整理する

スクリプトで$1 変数をチェックすることは、外部関数呼び出しをルーティングする1つの方法であることを学びました。 ただし、シェルスクリプトを作成するときに、他のスクリプトが一部の関数を再利用できる場合は、関数を別のスクリプトに移動し、現在のスクリプトから調達することを検討する必要があります。

これにより、機能の共有がはるかに簡単でクリーンになります。

たとえば、myScript.shlogger.shsendEmail.shの2つのスクリプトに分割できます。

$ cat logger.sh 
#!/bin/bash
log_info() {
    local MSG="$1"
    printf "%s - [INFO] %s\n" "$(date)" "$MSG"
}

log_error() {
    local MSG="$1"
    printf "%s - [ERROR] %s\n" "$(date)" "$MSG" >&2
}
$ cat sendEmail.sh 
#!/bin/bash

source logger.sh

VAR="Inside Script"
# simulate to send emails to all users
echo "Sending emails to all email addresses in users.txt..."
echo "Done"
# log
log_info "Emails have been sent to all users."

このように、ログ関数を呼び出したい場合は、 source logger.sh

$ . logger.sh && log_info "I am an INFO message outside the script file" 
Tue 25 Aug 2020 10:47:34 PM CEST - [INFO] I am an INFO message outside the script file

7. 結論

この記事では、コマンドラインからシェルスクリプトで定義された関数を呼び出す方法を学びました。

最初は、スクリプトを調達することが最も簡単なアプローチであると考えるかもしれません。 ただし、スクリプトをソーシングすると、スクリプト内のすべてのコマンドが実行され、シェルですでに定義されている変数を上書きするという望ましくない副作用が発生する可能性があることに注意してください

後で、スクリプト全体を調達せずに関数を呼び出す方法も見てきました。 最後に、関数を個別のスクリプトファイルに移動することで、コマンドラインまたは他のスクリプトから安全にそれらを調達できることを確認しました。