web-dev-qa-db-ja.com

* .shを使用して異なるディレクトリで複数の.shファイルを実行する方法

ディレクトリパターンに存在する場合、すべての.shファイルを実行できるシェルスクリプトを作成する必要があります。何かのようなもの:

##!/bin/bash
sh /var/scripts/*/my_*.inc.sh

しかし、上記のスクリプトは1つのファイルのみを実行します。発生した最初のファイル(最新の変更日で選択されたようです)は私のポイントではありません。ルールに一致するすべてのファイルを実行する必要があります。

3

コードが機能しないのは、ファイル名のグロビングパターンが展開したときに何が起こるかが原因です。

パターンは、一致するパス名の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に変更するか、スクリプトのソースに必要な他のインタープリターを変更する必要があることは明らかです。

8
Kusalananda

.inc.sh拡張子は、これらの.inc.shファイルがincludedであること、つまり、その内容がone Shellによってシェルコードとして評価されることを示唆していますインタープリター。同じシェルインタープリターがfunctionsvariablesaliases ...で定義された他のコードを実行できるようにします。

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を呼び出すか、構文エラーがあるか、または呼び出された特殊な組み込み関数が失敗した場合は、スクリプトも終了します。

5

シェルスクリプト(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
3
user000001

それが最良の解決策であるかどうかはわかりませんが、xargsは一般に次のようなものに使用できます。

ls /var/scripts/*/my_*.inc.sh | xargs -I {} sh {}

@ user000001で指摘されているように、これは改行文字を含むファイル名では機能しません。

2

次のように、forループを使用して、findで見つかったパス内のすべてのシェルファイルを(再帰的に)反復できます。

for i in $(find /var/scripts/ -type f -name '*.sh')
  do sh "$i"
done
0
GoofyEscape