どこでもパターンをコピーして貼り付けるのではなく、中かっこやワイルドカードパターンに一致する一連のファイルに対して一連のコマンドを実行したいと思います。パターンを変数に入れてこれを達成しようとしましたが、変数を元のパターンのように機能させる方法はわかりません。どうすればいいですか?それとも別の方法でこの問題を解決できますか?
たとえば、スカラー変数で定義されたパターンとcat
一致するファイルに対してどのように実行できますか?src/component\ {a,b,c}/*.c
component_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[@]}"
配列に何百または数千のパス名が含まれていない場合。
答え2
パラメータだけでなく、コマンド全体を評価します。
eval ">\"dist/support.txt\" cat ${component_data_pattern}";
test -s "dist/all.c";
私はこれが好きではありませんが、動作します。中かっことファイル全体を含むパターンを拡張しようとしていることを考慮すると(そのうちの1つは変数拡張の前に発生し、もう1つは後で発生する)、おそらく次のような他のオプションはありません。完全なコマンド呼び出し文字列を保存し、その文字列をまたはeval
のパラメータとして使用しますbash -c
。内部引用符をエスケープすることを忘れないでください\"
。
上記の例には他のパラメータはありません。他のパラメータがあり、これらのパラメータもある種の置換を使用している場合は、コマンドが最終的に評価されコンテキストで解釈されるまで拡張されないように、これらのパラメータをエスケープする必要があります(\$
または使用)\*
。\{
\}
readonly annoying_arg="$PWD/src/docs/test data.txt";
eval ">\"dist/support.txt\" cat ${component_data_pattern} \"\$annoying_arg\"";
test -s "dist/all.c";