ディレクトリパターンに存在する場合、すべての.sh
ファイルを実行できるシェルスクリプトを作成する必要があります。何かのようなもの:
##!/bin/bash
sh /var/scripts/*/my_*.inc.sh
しかし、上記のスクリプトは1つのファイルのみを実行します。発生した最初のファイル(最新の変更日で選択されたようです)は私のポイントではありません。ルールに一致するすべてのファイルを実行する必要があります。
コードが機能しないのは、ファイル名のグロビングパターンが展開したときに何が起こるかが原因です。
パターンは、一致するパス名のlistに展開されます。スクリプトのコードは、このリストを使用してsh
を呼び出します。つまり、最初に一致した名前をスクリプトとして実行し、他の名前をargumentsとしてそのスクリプトに与えます。
sh /var/scripts/dir/my_first.inc.sh /var/scripts/dir/my_second.inc.sh /var/scripts/dir/my_third.inc.sh
代わりに、一致する名前を繰り返すだけです。
#!/bin/sh
for name in /var/scripts/*/my_*.inc.sh; do
sh "$name"
done
これは、一致する名前のそれぞれが、sh
ではなくbash
によって実行されるスクリプトであると想定しています。個々のファイルが実行可能で、適切な#!
- lineがある場合は、上記のループのスクリプトの呼び出しからsh
を削除します。ファイルが「ドットスクリプト」、つまりソースにする必要があるスクリプトの場合、現在のスクリプトの環境でスクリプトを実行するには、代わりにsh
を.
に置き換えます。
上記のスクリプトはsh
機能を使用しないため、bash
スクリプト(#!/bin/sh
)にすることができます。他のスクリプトが「ドットスクリプト」の場合は、これを#!/bin/bash
に変更するか、スクリプトのソースに必要な他のインタープリターを変更する必要があることは明らかです。
.inc.sh
拡張子は、これらの.inc.sh
ファイルがincludedであること、つまり、その内容がone Shellによってシェルコードとして評価されることを示唆していますインタープリター。同じシェルインタープリターがfunctions、variables、aliases ...で定義された他のコードを実行できるようにします。
bash
の構文は、特にPOSIXモードが有効な場合(sh
が実際にはbash
インタープリターで、sh
として呼び出された場合)、特にsh
構文との下位互換性があるため、これらの関数はおそらくsh
言語で記述されていても、 bash
インタープリターがそれらを解釈します。
bash
(POSIXモードでない場合)とsh
の大きな違いの1つは、bash
ではスクリプトでalias
の拡張が有効にならないことです。 expand_aliases
オプションまたはposix
オプションを設定することで回避できます。
#! /bin/bash -
set -o posix # increase POSIX sh compatibility, some but not all bash-specific
# extensions are still available.
for file in /var/scripts/*/my_*.inc.sh; do
. "$file"
done
# rest of the code that uses the functions/variables, etc defined
# in the files sourced above with the "." special builtin
一致するファイルがない場合、文字どおり.
という存在しないファイルを取得してシェルを終了しようとすると、/var/scripts/*/my_*.inc.sh
コマンドは失敗します。
その代わりに、あなたができることを何もしたくない場合:
shopt -s nullglob
files=(/var/scripts/*/my_*.inc.sh)
shopt -u nullglob
for file in "${files[@]}"; do
command . "$file"
done
nullglob
を使用すると、一致しないグロブは、展開されていないパターンではなく空のリストに展開されます。 command
接頭辞を使用すると、.
などの特殊なビルトインが失敗しても($file
が読み取れない場合など)、シェルは終了しません(エラーは引き続き報告されます)。
.inc.sh
のサブディレクトリだけでなく/var/scripts
ファイルを再帰的に検索するには、globstar
オプションを使用して**
演算子を有効にします。
shopt -s nullglob globstar
files=(/var/scripts/**/my_*.inc.sh)
shopt -u nullglob globstar
いずれの場合も、名前が.
(非表示のもの)で始まるディレクトリおよびファイルは省略されます。含める場合は、dotglob
オプションを使用します。
ソースのスクリプトのいずれかがexit
またはexec
を呼び出すか、構文エラーがあるか、または呼び出された特殊な組み込み関数が失敗した場合は、スクリプトも終了します。
シェルスクリプト(bashを含む)でファイルを反復処理する最も安全な方法は、forループでパス名展開(グロビング)を使用することです。正しいコードは次のとおりです。
for file in /var/scripts/*/my_*.inc.sh
do
echo "Executing $file"
"$file"
done
これは、スペース、アスタリスク、改行などの特殊文字を含むパスとファイル名を処理します。
再帰的な検索を行う場合、またはグロビングが提供できないオプションを使用する場合は、findを-exec
オプションと一緒に使用できます(以下は、最近のGNU findと想定しています):
find a/ -executable -type f -name '*.sh' -exec {} \;
それでもfindが必要な場合は、ファイルごとに複数のコマンドを実行するためにread
を使用できますが、すべてのファイル名を処理するオプションに注意する必要があります(以下ではbashとGNU検索):
#!/bin/bash
find a/ -executable -type f -name '*.sh' -print0 |
while IFS= read -r -d $'\0' file
do
echo "Executing $file"
"$file"
done
それが最良の解決策であるかどうかはわかりませんが、xargs
は一般に次のようなものに使用できます。
ls /var/scripts/*/my_*.inc.sh | xargs -I {} sh {}
@ user000001で指摘されているように、これは改行文字を含むファイル名では機能しません。
次のように、for
ループを使用して、find
で見つかったパス内のすべてのシェルファイルを(再帰的に)反復できます。
for i in $(find /var/scripts/ -type f -name '*.sh')
do sh "$i"
done