重複することなく中かっこ+グローブパターンに一致するファイルに対して複数のコマンドを実行します。

重複することなく中かっこ+グローブパターンに一致するファイルに対して複数のコマンドを実行します。

どこでもパターンをコピーして貼り付けるのではなく、中かっこやワイルドカードパターンに一致する一連のファイルに対して一連のコマンドを実行したいと思います。パターンを変数に入れてこれを達成しようとしましたが、変数を元のパターンのように機能させる方法はわかりません。どうすればいいですか?それとも別の方法でこの問題を解決できますか?

たとえば、スカラー変数で定義されたパターンとcat一致するファイルに対してどのように実行できますか?src/component\ {a,b,c}/*.ccomponent_source_code

例 コンテキストと再生

set -euo pipefail;
mkdir "src/" "dist/";
trap 'rm -r "src/" "dist/"' EXIT;

私は次のように構成されたプロジェクトを持っています(より便利なコンテンツが含まれています)。

>"src/README.md"                 date;
mkdir "src/component a/";
>"src/component a/program.c"     date;
>"src/component a/tests.c"       date;
>"src/component a/budget\$.txt"  date;
mkdir "src/component b/";
>"src/component b/program.c"     date;
>"src/component b/tests.c"       date;
>"src/component b/braces{}.txt"  date;
mkdir "src/component c/";
>"src/component c/program.c"     date;
>"src/component c/tests.c"       date;
>"src/component c/test data.txt" date;
mkdir "src/docs";
>"src/docs/test data.txt"        date;

複数のコンポーネントの関連ファイルをターゲットにする必要があるビルド手順があります。これらのファイルセットと一致するように、中かっこ+globパターンを使用して変数を定義しました。

readonly component_paths_pattern="src/component\ {a,b,c}";
readonly component_data_pattern="${component_paths_pattern}/*.txt";
readonly component_code_pattern="${component_paths_pattern}/*.c";

これらのパターンをサンプルコマンドに手動でコピーすると、予想されるファイルと一致します。

>"dist/support.txt" cat src/component\ {a,b,c}/*.txt;
test -s "dist/all test data.txt";

>"dist/all.c" cat src/component\ {a,b,c}/*.c;
test -s "dist/all.c";

一度だけ参照してもかまいませんが、実際にはビルドスクリプトのさまざまな部分で同じファイルセットを複数回参照する必要があるため、これらのパターンを変数で再利用したいと思います。しかし、私はそれを動作させる方法を知りませんでした。

set -x;

失敗したソリューションを試す

引用符なしの変数の拡張(分割+ワイルドカード)

>"dist/support.txt" cat ${component_data_pattern};

パターンにスペースが含まれているので、これが失敗すると思います。したがって、2つの別々のglobパターンパラメータに分割され、どちらも一致しません。

+ cat 'src/component\' '{a,b,c}/*.txt'
cat: src/component\: No such file or directory
cat: {a,b,c}/*.txt: No such file or directory

参照変数の拡張

>"dist/support.txt" cat "${component_data_pattern}";

中かっこ拡張が変数拡張の前に発生するので、これは失敗すると思います。したがって、中括弧はここで拡張する機会を得ません。

+ cat 'src/component\ {a,b,c}/*.txt'
cat: src/component\ {a,b,c}/*.txt: No such file or directory

パラメータリストのEvalとEcho

>"dist/support.txt" cat $(eval "echo ${component_data_pattern}");

サブコマンド拡張を引用しないと、生成されたパスの一部にスペースが含まれて別々の引数に分割されるため、これは失敗すると思います。

++ eval 'echo src/component\ {a,b,c}/*.txt'
+++ echo 'src/component a/budget$.txt' 'src/component b/braces{}.txt' 'src/component c/test data.txt'
+ cat src/component 'a/budget$.txt' src/component 'b/braces{}.txt' src/component c/test data.txt
cat: src/component: No such file or directory
[...]
>"dist/support.txt" cat "$(eval "echo ${component_data_pattern}")";

サブコマンド拡張を引用すると、すべてのパスが単一の文字列にリンクされ、間違ったパスが長くなるため失敗するようです。

++ eval 'echo src/component\ {a,b,c}/*.txt'
+++ echo 'src/component a/budget$.txt' 'src/component b/braces{}.txt' 'src/component c/test data.txt'
+ cat 'src/component a/budget$.txt src/component b/braces{}.txt src/component c/test data.txt'
cat: src/component a/budget$.txt src/component b/braces{}.txt src/component c/test data.txt: No such file or directory

パラメータリストのEvalおよびPrintf%q

同様の理由でprintf '%q '失敗の代わりに使用してください。echo

>"dist/support.txt" cat "$(eval "printf '%q ' ${component_data_pattern}")";
++ eval 'printf '\''%q '\'' src/component\ {a,b,c}/*.txt'
+++ printf '%q ' 'src/component a/budget$.txt' 'src/component b/braces{}.txt' 'src/component c/test data.txt'
+ cat 'src/component\ a/budget\$.txt src/component\ b/braces\{\}.txt src/component\ c/test\ data.txt '
cat: src/component\ a/budget\$.txt src/component\ b/braces\{\}.txt src/component\ c/test\ data.txt : No such file or directory
>"dist/support.txt" cat $(eval "printf '%q ' ${component_data_pattern}");
++ eval 'printf '\''%q '\'' src/component\ {a,b,c}/*.txt'
+++ printf '%q ' 'src/component a/budget$.txt' 'src/component b/braces{}.txt' 'src/component c/test data.txt'
+ cat 'src/component\' 'a/budget\$.txt' 'src/component\' 'b/braces\{\}.txt' 'src/component\' 'c/test\' data.txt
cat: src/component\: No such file or directory
[...]

ベストアンサー1

配列を使用し、ファイル名のワイルドカードパターンを変数に保存しないでください(一致するパス名に拡張するようにしてください)。

component_dirs=( 'src/component '{a,b,c} )

component_data=()
component_code=()

for dir in "${component_dirs[@]}"; do
    component_data+=( "$dir"/*.txt )
    component_code+=( "$dir"/*.c   )
done

それからあなたは次のことをすることができます

cat "${component_data[@]}"

配列に何百または数千のパス名が含まれていない場合。

おすすめ記事