関数定義のみを含むスクリプトを作成しますか?または、関数本文のコードをスクリプトに移動しますか?

関数定義のみを含むスクリプトを作成しますか?または、関数本文のコードをスクリプトに移動しますか?

いくつかの背景:Bashで再利用可能なコードを書くとき、私はいくつか見ました。 複数の関数定義を含むシェルスクリプト。時には、スクリプトに定義された関数が関連しているのか、スクリプトが実際に混乱しているのか、複数の関数を1つのスクリプトで構成する方法がわからないのかわかりません(この目的に関する提案はありますか?)。だから、1つの関数定義だけを含むスクリプトを書くつもりですが、複数のファイルを整理する必要があるようです。 1つのスクリプトに複数の関数定義を構成することと、それぞれ関数定義を含む複数のスクリプトファイルを構成する方法のどちらが優れていますか?

以下では、1つの関数定義のみを含むスクリプトを作成するとします。


私の実際の質問ここから始めましょう:

執筆中1つの関数定義のみを含むスクリプト、私は書くことができます

#! /bin/bash

function myfunc() {
    echo $1, $2, $3
    # do some work...
}

それから私は書くことができます。

source /path/to/myscript
myfunc 1 2 3

あるいは、関数定義を削除して関数本体のコードをスクリプトに移動して、同じコード再利用性目標を達成することもできます。

#! /bin/bash

echo $1, $2, $3
# do some work...

それから私は書くことができます。

source /path/to/myscript 1 2 3

? 2番目の方法はスクリプトの内容や使い方の面で最初の方法よりもきれいに見えますが、何らかの理由で2番目の方法を嫌う人がいるかどうかはわかりません。上記の2つの方法の長所と短所が何であるかを知りたいです。実際にどのようなアドバイスをしてくれますか?

実際に最初のアプローチを使用している場合は、スクリプト名を関数と同じにしますか?つまり、スクリプトにという関数が含まれている場合は、myfuncスクリプト名もとして指定しますかmyfunc

ありがとうございます。

答え1

私は状況に応じて両方します。ほとんどの場合、これらの関数を使用する同じスクリプトで宣言しますが、すべてのスクリプト間で共有したい変数と関数ファイルを含むスクリプト「キット」があります。

機能するかどうか

関数の主な目的はDRY(繰り返しません)です。スクリプトで何度も使用されるコードがある場合は、関数に存在する必要があります。

繰り返されないコードで関数を使用することは好ましい問題です。何人かの人々(私自身を含む)はそれがよりきれいだと思います。また、「実際の」プログラミング言語が使用される方法に似ていると思います。

Googleのシェルスタイルガイドコードに1つ以上の関数が含まれている場合は、「main」関数をすべてのコードの周りのラッパーとして使用する必要があることに注意してください。 (本質的に、スクリプトは単一の呼び出しで終わる一連の関数宣言ですmain。)

単一スクリプト内で関数宣言(またはコードを書く)

  • シンプルさ(コードを見る人は誰でも各関数の機能を簡単に追跡できます)
  • 移植性の向上(あるシステムから別のシステムに1つのファイルのみを移動)

単一のタスクを実行するためのスタンドアロンスクリプトを作成する場合は、これが正しい選択である可能性があります。

別のファイルへの関数宣言

  • DRY(繰り返しないでください)
  • 管理が簡単になります。

多くの共通機能を共有するツールセットを作成する場合は、これが正しい選択である可能性があります。

私の例には、ツールキットのスクリプトのすべてまたは少なくともほとんどで使用される機能があります。これには、在庫管理システムを照会し、サーバーに関する情報(IPアドレス、オペレーティングシステムのバージョンなど)を返す機能が含まれます。この情報は多くのツールに役立つので、すべてのファイルではなく1つのファイルでこれらの関数を宣言するのは妥当です。別のファイル。また、最近の在庫管理システムを変更して10個以上の異なるファイルを変更する代わりに、新しいシステムに照会するために共通ファイルのみを変更する必要があり、他のファイルは変更なしで機能しました。

これのいくつかの欠点は、より複雑であることです。各ファイルには、この共通ファイルをインポートするための次のステートメントがあります。

if [[ -f "${0%/*}/lib/common.sh" ]]; then
    . "${0%/*}/lib/common.sh"
else
    echo "Error! lib/common.sh not found!"
    exit 1
fi

ユーザーがツールキットをインポートしてディレクトリ構造を変更したり、ディレクトリ構造全体をインポートできなかった場合、パブリックファイルをインポートできないため、ツールは期待どおりに機能しません。

答え2

これは、はいまたはいいえタイプの答えではなく、長い答えになります。これは、ワークフローを選択するときに考慮する必要があるBash機能が扱うさまざまなプログラミングの側面を説明します。

最後に、個別のパラメータ化されたソースファイルで単一の関数定義のデモを見つけることができます。


機能は次のとおりです。

  • 名前付き
  • 詰まったコード化
  • これができる再利用(複数回呼び出されます)

上記を考慮すると、どのような利点があり、選択肢がありますか?

  1. 論理的整理するコード化

    特定の目的を実行するコマンドの配置を一緒にグループ化することは非常に意味があります。結局、それは言葉の意味の一つです。機能-何かこのような機能があります。。ただし、同じ目標を異なる方法で達成することもできます。

    • コマンドは直感的にグループ化できます。連続した行を占有し、空の行とコメントでコードの残りの部分から分離されます。または同じ行に入れて、最後にコメントを追加してください。
    • 別々のファイルにグループ化できます。処刑されたまたは源泉
    • 中かっこを使用してブロックできます{ ... }
    • グループ化して個別に実行することもできます。サブシェルサブシェルかっこ内に配置されている場合( ... )。 1
  2. バッチジョブ

    関数には、標準の Bash 演算子を使用して簡単に一括入出力リダイレクトができるという利点があります。ブロック、サブシェル、スクリプトの実行、ソースも問題ありませんが、純粋なビジュアルコマンドグループの場合、I / Oファイル記述子の変更はexec

  3. ローカライズ

    これは単純ブロックと比較して関数の特別な点の1つです。変数を生成する機能local。これは、変数の可視性を最小限に抑え、より広い名前空間の混乱を減らし、分離を導入するため、一般的に良いプログラムです。これは最終的に貴重なセキュリティ機能です。

    ここでは、個々のスクリプトとサブシェルを選択できます。位置パラメータについては、ソースファイルにも同様に適用されます。

  4. パラメータ化する

    各関数には固有の位置パラメータセットなどがあります$1$2これにより、コマンド呼び出し内で関数の動作を明示的に変更できます。

    ここには、スタンドアロンスクリプトとソースファイルがあります。

  5. パスワード識別する

    機能名前これは明示的な認識だけでなく実行能力も意味します。そして何度も必要でした。ブロック、サブシェル、プレーンテキストのグループ化は、すべて{}ループで囲まれていない限り、この機能を提供しません。スクリプトとソースファイルは大丈夫ですが。

  6. 情報

    最後に、関数名と関数に含めることができるコメントは、コードの理解度に影響します。他のすべてのオプションはありますが、サブシェルや{}ブロックなどの匿名ソリューションでは、コードブロックの正式なタイトル(およびアドレス)を提供できません。


次の例は、単一の関数を別々のソースファイルに適切に配置する方法を示しています。

case "$1" in
    a)
        f () { echo "This function was defined in a)."; }
        ;;
    b)
        f () { echo "This function was defined in b)."; }
        ;;
    *)
        f () { echo "This function was defined with no recognised argument."; }
        ;;
esac

この条件付き定義は関数ファクトリと同じです。他の場所から取得したファイルは、fその場所に機能をインストールします。これらのコードはすべて他の関数に含めることができ、現在ではあまり違いはありません。しかし、関数ジェネレータがそれ自体が1つのオブジェクトになるのに十分な大きさになると、このアプローチは関数とソースファイルの2つの利点を組み合わせます。


また、見ることができます

実際、Bashは同様の関数定義を受け入れます。このようf () ( ...;)に定義すると、f関数は独自のサブシェルで実行されます。

関連情報