大きなスクリプトを複数のスクリプトに分割し、それらをメインスクリプトに取り込むのは一般的ですか?


23

現時点では、より大きなBashスクリプト(私のオープンソースプロジェクト)を開発しており、混乱になり始めています。ロジックを関数に分割し、ローカル変数を使用して、ほんの一握りのグローバル変数を宣言しただけです。それでも、メンテナンスはかなり難しくなっています。

スクリプトを複数のスクリプトに分割し、メインスクリプトでソース化することを考えました(他の言語でのインポートと同様)。

しかし、これは実行可能なアプローチかどうか疑問に思います。第一に、複数のスクリプトを調達すると、スクリプトの実行時間が大幅に遅くなる可能性があり、第二に、配布が難しくなります。

だから、これは良いアプローチですか?他の(オープンソース)プロジェクトも同じようにやっていますか?


4
本当に長いシェルスクリプトを探しに行きました、3500行と125 KBで、それを維持しようとは思いません。シェルは、プログラムを接続するときに非常に使いやすいですが、計算を実行しようとすると、かなりいものになります。私はあなたが持っているコードの大部分が動作し、それを移植するコストが高いことを知っていますが、将来何か他のものを検討したいと思うかもしれません。
msw

1
同じ問題に遭遇しました。中規模のbashプロジェクトsourceforge.net/projects/duplexprがあります。現在、各スクリプトは自己完結型ですが、すべての共通機能を個別のファイルに移動し、複数の場所で更新する必要をなくすために必要な場所にそれらを含めることを考えています。一般に、ソースを取得するよりも、連続する各スクリプトを呼び出す方が良いと思います。部品を独立して実行できるようにします。次に、変数を引数として、またはパラメーターファイルで渡す必要があります(または、スタンドアロンにしたくない場合は、単にエクスポートすることができます)。
ジョー

回答:


13

はい、それは一般的な慣行です。たとえば、Unixの初期には、システムをブートフェーズからマルチユーザー操作に導くシェルコードは単一のファイルでした/etc/rc。現在、ブートプロセスは、中央の場所から必要に応じて供給される共通の関数と変数を使用して、機能ごとに分割された多くのシェルスクリプトによって制御されています。Linuxディストリビューション、Mac、BSDはすべて、このアプローチをさまざまな程度で採用しています。


2
ただし、その場合は再利用性が向上します。異なるスクリプトは、シェル関数の共通ライブラリーを使用します。
ステファンシャゼル

16

その時点でシェルは仕事に適したツールですか?成長するコードの問題にぶつかった開発者として、書き直しは考慮すべきではなく、代わりに、アプリケーションを成長させるために探しているスケールに適したものに分割することを検討することができます-おそらくpythonまたはrubyまたはperl ?

シェルはユーティリティ言語であり、スクリプト言語です。そのため、これらのサイズに拡張することは困難です。


これはまさに私が投稿しようとしていたものです。
zwol

特に、Solarisのような古いOSでもperlやpythonなどで出荷されていることを考えると。古いシステムがシェルスクリプトを使用した理由の1つは、シェルが常に使用可能であることが保証されていたが、HP-UXやSolaris、AIXなどのより大きなUnicesが他のツールを含むことを保証できなかったためです。
ティムケネディ

7

メンテナンスが容易になる場合は、両方を使用できます。簡単に保守できるように論理部分に分割してから、Makefileを作成して(たとえば)配布用にまとめます。関数をインクルードファイルから出力ファイルにコピーするための簡単なスクリプトを書くことができます。source行またはちょうどこのような些細な何かを(あなたに、再tabifyこれをする必要がありますmakeタブが必要です):

all: myscript

myscript: includes/* body/*
    cat $^ > "$@" || (rm -f "$@"; exit 1)

次に、「ソース」バージョン(編集に使用)と「バイナリ」バージョン(簡単なインストールに使用)があります。


1
あぁ!単純な猫のアプローチを使用する他の誰か:)
クレイトンスタンリー

4

スクリプトは、説明したとおりに分割される可能性があります-ほぼ何でもできます。「良いアプローチ」は、大きなスクリプトを区分化し、その一部を別のプロセスとして実行し、IPCメカニズムを介して通信できるようにすることです。

さらに、シェルスクリプトの場合は、単一のファイルとしてパッケージ化します。あなたが言うように、それは配布をより困難にします:「ライブラリ」スクリプトがどこにあるかを知る必要があります-シェルスクリプトには良い標準がありません-またはユーザーにパスを正しく設定することに依存します。

これをすべて処理するインストールプログラムを配布して、ファイルを抽出し、適切な場所に配置して、ユーザーにexport PROGRAMDIR=$HOME/lib/PROGRAM〜/ .bashrcファイルに何かを追加するように指示することができます。その後、メインプログラムが$PROGRAMDIR設定されていないか、予期したファイルが含まれていない場合、メインプログラムが失敗する可能性があります。

他のスクリプトをロードするオーバーヘッドについてはあまり心配しません。オーバーヘッドは実際にはファイルを開くだけです。特に関数定義の場合、テキストの処理は同じです。


1

一般的な慣行であろうとなかろうと、一連の輸出品以外のものを調達することは良い考えだとは思いません。環境やその他の変数設定によりソースコードがソースコードに大きく依存するため、ソースコードによるコードの実行は混乱を招き、再利用が制限されることがわかりました。

アプリケーションをより小さな自己完結型のスクリプトに分割し、一連のコマンドとして実行する方が良いでしょう。これにより、各コマンド呼び出しの間にファイル、ログなどを調べる対話型シェルで各自己完結型スクリプトを実行できるため、デバッグが容易になります。単一の大きなアプリケーションスクリプトは、デバッグが終了した後に一連のコマンドを実行するだけの単純な制御スクリプトに変わります。

より小さな自己完結型のスクリプトのグループは、インストールが困難な問題に遭遇します。


1

スクリプトを入手する代わりに、単に引数を使用してスクリプトを呼び出すことができます。すでにほとんどの機能をシェル関数に分割している場合は、おそらくこれを既に実行できる状態に近いでしょう。次のBashのスニペットにより、スクリプトで宣言された関数をサブコマンドとして使用できます。

if [[ ${1:-} ]] && declare -F | cut -d' ' -f3 | fgrep -qx -- "${1:-}"
then "$@"
else main "$@" # Try the main function if args don't match a declaration.
fi

しない理由は、source環境とオプションの汚染を避けるためです。


0

大きなbashスクリプトを複数のファイルに分割し、1つの結果スクリプトに組み込む方法の例を次に示します。https//github.com/zinovyev/bash-project

Makefileはこの目的のために使用します:

TARGET_FILE = "target.sh"
PRJ_SRC = "${PWD}/src/main.sh"
PRJ_LIB = $(shell ls -d ${PWD}/lib/*) # All files from ./lib

export PRJ_LIB

SHELL := /bin/env bash
all: define_main add_dependencies invoke_main

define_main:
    echo -e "#!/usr/bin/env bash\n" > ${TARGET_FILE}
    echo -e "function main() {\n" >> ${TARGET_FILE}
    cat "${PRJ_SRC}" | sed -e 's/^/  /g' >> ${TARGET_FILE}
    echo -e "\n}\n" >> ${TARGET_FILE}

invoke_main:
    echo "main \$$@" >> ${TARGET_FILE}

add_dependencies:
    for filename in $${PRJ_LIB[*]}; do cat $${filename} >> ${TARGET_FILE}; echo >> ${TARGET_FILE}; done
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.