Running multiple command against files matching a brace+glob pattern without repeating it
I would like to run a series of commands against a set of files matched by a braces-and-globs pattern, without copy-pasting the pattern all over the place. I've been trying to do this by putting the pattern in a variable, but haven't been able to figure out how to make that variable work like the original pattern. How could I do that, or otherwise solve this problem?
For example, how can I run cat
against files matching the pattern src/component {a,b,c}/*.c
defined in the scalar variable component_source_code
?
Example Context and Reproduction
set -euo pipefail;
mkdir "src/" "dist/";
trap 'rm -r "src/" "dist/"' EXIT;
I have a project whose structure resembles the following (though with more useful contents).
>"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;
I have build steps that need to target relevant files in multiple components.
I have defined variables with brace+glob patterns to match such sets of files.
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";
When I manually copy these patterns into an example command, they match the expected files.
>"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";
That would be okay if I only had to reference them once, but in reality I need to reference the same sets of files several times from different parts of the build script, hence my desire to reuse the patterns in a variable. However, I haven't been able to figure out how to make this work properly.
set -x;
Unsuccessfully Attempted Solutions
Unquoted Variable Expansion (Split+Globbing)
>"dist/support.txt" cat ${component_data_pattern};
I think this fails because the pattern contains a space, so it's split into two separate glob pattern arguments, neither of which match anything on their own.
+ 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
Quoted Variable Expansion
>"dist/support.txt" cat "${component_data_pattern}";
I think this fails because brace expansion occurs before variable expansion, so the braces have no chance to be expanded here.
+ cat 'src/component {a,b,c}/*.txt'
cat: src/component {a,b,c}/*.txt: No such file or directory
Eval and Echo in Argument List
>"dist/support.txt" cat $(eval "echo ${component_data_pattern}");
Without quoting the subcommand expansion, I think this fails because some generated paths include spaces, causing them to be split into separate arguments.
++ 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}")";
If I do quote the subcommand expansion, I think it fails because this joins all of the paths into a single string, producing a long invalid path.
++ 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 and Printf %q in Argument List
Using printf '%q '
instead of echo
fails for similar reasons.
>"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
[...]
bash wildcards variable-substitution brace-expansion
add a comment |
I would like to run a series of commands against a set of files matched by a braces-and-globs pattern, without copy-pasting the pattern all over the place. I've been trying to do this by putting the pattern in a variable, but haven't been able to figure out how to make that variable work like the original pattern. How could I do that, or otherwise solve this problem?
For example, how can I run cat
against files matching the pattern src/component {a,b,c}/*.c
defined in the scalar variable component_source_code
?
Example Context and Reproduction
set -euo pipefail;
mkdir "src/" "dist/";
trap 'rm -r "src/" "dist/"' EXIT;
I have a project whose structure resembles the following (though with more useful contents).
>"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;
I have build steps that need to target relevant files in multiple components.
I have defined variables with brace+glob patterns to match such sets of files.
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";
When I manually copy these patterns into an example command, they match the expected files.
>"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";
That would be okay if I only had to reference them once, but in reality I need to reference the same sets of files several times from different parts of the build script, hence my desire to reuse the patterns in a variable. However, I haven't been able to figure out how to make this work properly.
set -x;
Unsuccessfully Attempted Solutions
Unquoted Variable Expansion (Split+Globbing)
>"dist/support.txt" cat ${component_data_pattern};
I think this fails because the pattern contains a space, so it's split into two separate glob pattern arguments, neither of which match anything on their own.
+ 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
Quoted Variable Expansion
>"dist/support.txt" cat "${component_data_pattern}";
I think this fails because brace expansion occurs before variable expansion, so the braces have no chance to be expanded here.
+ cat 'src/component {a,b,c}/*.txt'
cat: src/component {a,b,c}/*.txt: No such file or directory
Eval and Echo in Argument List
>"dist/support.txt" cat $(eval "echo ${component_data_pattern}");
Without quoting the subcommand expansion, I think this fails because some generated paths include spaces, causing them to be split into separate arguments.
++ 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}")";
If I do quote the subcommand expansion, I think it fails because this joins all of the paths into a single string, producing a long invalid path.
++ 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 and Printf %q in Argument List
Using printf '%q '
instead of echo
fails for similar reasons.
>"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
[...]
bash wildcards variable-substitution brace-expansion
add a comment |
I would like to run a series of commands against a set of files matched by a braces-and-globs pattern, without copy-pasting the pattern all over the place. I've been trying to do this by putting the pattern in a variable, but haven't been able to figure out how to make that variable work like the original pattern. How could I do that, or otherwise solve this problem?
For example, how can I run cat
against files matching the pattern src/component {a,b,c}/*.c
defined in the scalar variable component_source_code
?
Example Context and Reproduction
set -euo pipefail;
mkdir "src/" "dist/";
trap 'rm -r "src/" "dist/"' EXIT;
I have a project whose structure resembles the following (though with more useful contents).
>"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;
I have build steps that need to target relevant files in multiple components.
I have defined variables with brace+glob patterns to match such sets of files.
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";
When I manually copy these patterns into an example command, they match the expected files.
>"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";
That would be okay if I only had to reference them once, but in reality I need to reference the same sets of files several times from different parts of the build script, hence my desire to reuse the patterns in a variable. However, I haven't been able to figure out how to make this work properly.
set -x;
Unsuccessfully Attempted Solutions
Unquoted Variable Expansion (Split+Globbing)
>"dist/support.txt" cat ${component_data_pattern};
I think this fails because the pattern contains a space, so it's split into two separate glob pattern arguments, neither of which match anything on their own.
+ 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
Quoted Variable Expansion
>"dist/support.txt" cat "${component_data_pattern}";
I think this fails because brace expansion occurs before variable expansion, so the braces have no chance to be expanded here.
+ cat 'src/component {a,b,c}/*.txt'
cat: src/component {a,b,c}/*.txt: No such file or directory
Eval and Echo in Argument List
>"dist/support.txt" cat $(eval "echo ${component_data_pattern}");
Without quoting the subcommand expansion, I think this fails because some generated paths include spaces, causing them to be split into separate arguments.
++ 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}")";
If I do quote the subcommand expansion, I think it fails because this joins all of the paths into a single string, producing a long invalid path.
++ 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 and Printf %q in Argument List
Using printf '%q '
instead of echo
fails for similar reasons.
>"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
[...]
bash wildcards variable-substitution brace-expansion
I would like to run a series of commands against a set of files matched by a braces-and-globs pattern, without copy-pasting the pattern all over the place. I've been trying to do this by putting the pattern in a variable, but haven't been able to figure out how to make that variable work like the original pattern. How could I do that, or otherwise solve this problem?
For example, how can I run cat
against files matching the pattern src/component {a,b,c}/*.c
defined in the scalar variable component_source_code
?
Example Context and Reproduction
set -euo pipefail;
mkdir "src/" "dist/";
trap 'rm -r "src/" "dist/"' EXIT;
I have a project whose structure resembles the following (though with more useful contents).
>"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;
I have build steps that need to target relevant files in multiple components.
I have defined variables with brace+glob patterns to match such sets of files.
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";
When I manually copy these patterns into an example command, they match the expected files.
>"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";
That would be okay if I only had to reference them once, but in reality I need to reference the same sets of files several times from different parts of the build script, hence my desire to reuse the patterns in a variable. However, I haven't been able to figure out how to make this work properly.
set -x;
Unsuccessfully Attempted Solutions
Unquoted Variable Expansion (Split+Globbing)
>"dist/support.txt" cat ${component_data_pattern};
I think this fails because the pattern contains a space, so it's split into two separate glob pattern arguments, neither of which match anything on their own.
+ 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
Quoted Variable Expansion
>"dist/support.txt" cat "${component_data_pattern}";
I think this fails because brace expansion occurs before variable expansion, so the braces have no chance to be expanded here.
+ cat 'src/component {a,b,c}/*.txt'
cat: src/component {a,b,c}/*.txt: No such file or directory
Eval and Echo in Argument List
>"dist/support.txt" cat $(eval "echo ${component_data_pattern}");
Without quoting the subcommand expansion, I think this fails because some generated paths include spaces, causing them to be split into separate arguments.
++ 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}")";
If I do quote the subcommand expansion, I think it fails because this joins all of the paths into a single string, producing a long invalid path.
++ 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 and Printf %q in Argument List
Using printf '%q '
instead of echo
fails for similar reasons.
>"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
[...]
bash wildcards variable-substitution brace-expansion
bash wildcards variable-substitution brace-expansion
edited Dec 20 '18 at 19:33
asked Dec 19 '18 at 20:04
Jean
306
306
add a comment |
add a comment |
2 Answers
2
active
oldest
votes
Use arrays, and don't store the filename globbing patterns in variables (let them expand into the matching pathnames instead):
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
Then you could do, e.g.,
cat "${component_data[@]}"
unless that array contained many hundreds or thousands of pathnames.
interesting... I'll have to experiment with this. thanks for the suggestion.
– Jean
Dec 19 '18 at 21:01
1
Yep, my code is way cleaner this way. Thanks!
– Jean
Dec 20 '18 at 16:19
add a comment |
Eval the Entire Command (Not Just The Arguments)
eval ">"dist/support.txt" cat ${component_data_pattern}";
test -s "dist/all.c";
I don't like this, but it works. Given that we're trying to expand a pattern that includes both braces and file globs, one of which takes place before variable expansion and one of which takes place after, there may be no alternative to something like this: manually expanding the variable into a string that includes the entire command call, and using that string as the argument to eval
or bash -c
. Don't forget to escape any inner quotes with "
.
In the example above, there are no other arguments. If there are other arguments and those also use some kind of substitution, you'll need to escape those (with $
, *
, {
, or }
) so they're not expanded until the command is finally being evaluated and they can be interpreted in context.
readonly annoying_arg="$PWD/src/docs/test data.txt";
eval ">"dist/support.txt" cat ${component_data_pattern} "$annoying_arg"";
test -s "dist/all.c";
I got this solution working as I finished writing the question, but I don't like it very much. I would love any better suggestions that anybody might have.
– Jean
Dec 19 '18 at 20:05
add a comment |
Your Answer
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "106"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: false,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2funix.stackexchange.com%2fquestions%2f490000%2frunning-multiple-command-against-files-matching-a-braceglob-pattern-without-rep%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
2 Answers
2
active
oldest
votes
2 Answers
2
active
oldest
votes
active
oldest
votes
active
oldest
votes
Use arrays, and don't store the filename globbing patterns in variables (let them expand into the matching pathnames instead):
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
Then you could do, e.g.,
cat "${component_data[@]}"
unless that array contained many hundreds or thousands of pathnames.
interesting... I'll have to experiment with this. thanks for the suggestion.
– Jean
Dec 19 '18 at 21:01
1
Yep, my code is way cleaner this way. Thanks!
– Jean
Dec 20 '18 at 16:19
add a comment |
Use arrays, and don't store the filename globbing patterns in variables (let them expand into the matching pathnames instead):
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
Then you could do, e.g.,
cat "${component_data[@]}"
unless that array contained many hundreds or thousands of pathnames.
interesting... I'll have to experiment with this. thanks for the suggestion.
– Jean
Dec 19 '18 at 21:01
1
Yep, my code is way cleaner this way. Thanks!
– Jean
Dec 20 '18 at 16:19
add a comment |
Use arrays, and don't store the filename globbing patterns in variables (let them expand into the matching pathnames instead):
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
Then you could do, e.g.,
cat "${component_data[@]}"
unless that array contained many hundreds or thousands of pathnames.
Use arrays, and don't store the filename globbing patterns in variables (let them expand into the matching pathnames instead):
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
Then you could do, e.g.,
cat "${component_data[@]}"
unless that array contained many hundreds or thousands of pathnames.
edited Dec 19 '18 at 21:09
answered Dec 19 '18 at 20:37
Kusalananda
121k16229372
121k16229372
interesting... I'll have to experiment with this. thanks for the suggestion.
– Jean
Dec 19 '18 at 21:01
1
Yep, my code is way cleaner this way. Thanks!
– Jean
Dec 20 '18 at 16:19
add a comment |
interesting... I'll have to experiment with this. thanks for the suggestion.
– Jean
Dec 19 '18 at 21:01
1
Yep, my code is way cleaner this way. Thanks!
– Jean
Dec 20 '18 at 16:19
interesting... I'll have to experiment with this. thanks for the suggestion.
– Jean
Dec 19 '18 at 21:01
interesting... I'll have to experiment with this. thanks for the suggestion.
– Jean
Dec 19 '18 at 21:01
1
1
Yep, my code is way cleaner this way. Thanks!
– Jean
Dec 20 '18 at 16:19
Yep, my code is way cleaner this way. Thanks!
– Jean
Dec 20 '18 at 16:19
add a comment |
Eval the Entire Command (Not Just The Arguments)
eval ">"dist/support.txt" cat ${component_data_pattern}";
test -s "dist/all.c";
I don't like this, but it works. Given that we're trying to expand a pattern that includes both braces and file globs, one of which takes place before variable expansion and one of which takes place after, there may be no alternative to something like this: manually expanding the variable into a string that includes the entire command call, and using that string as the argument to eval
or bash -c
. Don't forget to escape any inner quotes with "
.
In the example above, there are no other arguments. If there are other arguments and those also use some kind of substitution, you'll need to escape those (with $
, *
, {
, or }
) so they're not expanded until the command is finally being evaluated and they can be interpreted in context.
readonly annoying_arg="$PWD/src/docs/test data.txt";
eval ">"dist/support.txt" cat ${component_data_pattern} "$annoying_arg"";
test -s "dist/all.c";
I got this solution working as I finished writing the question, but I don't like it very much. I would love any better suggestions that anybody might have.
– Jean
Dec 19 '18 at 20:05
add a comment |
Eval the Entire Command (Not Just The Arguments)
eval ">"dist/support.txt" cat ${component_data_pattern}";
test -s "dist/all.c";
I don't like this, but it works. Given that we're trying to expand a pattern that includes both braces and file globs, one of which takes place before variable expansion and one of which takes place after, there may be no alternative to something like this: manually expanding the variable into a string that includes the entire command call, and using that string as the argument to eval
or bash -c
. Don't forget to escape any inner quotes with "
.
In the example above, there are no other arguments. If there are other arguments and those also use some kind of substitution, you'll need to escape those (with $
, *
, {
, or }
) so they're not expanded until the command is finally being evaluated and they can be interpreted in context.
readonly annoying_arg="$PWD/src/docs/test data.txt";
eval ">"dist/support.txt" cat ${component_data_pattern} "$annoying_arg"";
test -s "dist/all.c";
I got this solution working as I finished writing the question, but I don't like it very much. I would love any better suggestions that anybody might have.
– Jean
Dec 19 '18 at 20:05
add a comment |
Eval the Entire Command (Not Just The Arguments)
eval ">"dist/support.txt" cat ${component_data_pattern}";
test -s "dist/all.c";
I don't like this, but it works. Given that we're trying to expand a pattern that includes both braces and file globs, one of which takes place before variable expansion and one of which takes place after, there may be no alternative to something like this: manually expanding the variable into a string that includes the entire command call, and using that string as the argument to eval
or bash -c
. Don't forget to escape any inner quotes with "
.
In the example above, there are no other arguments. If there are other arguments and those also use some kind of substitution, you'll need to escape those (with $
, *
, {
, or }
) so they're not expanded until the command is finally being evaluated and they can be interpreted in context.
readonly annoying_arg="$PWD/src/docs/test data.txt";
eval ">"dist/support.txt" cat ${component_data_pattern} "$annoying_arg"";
test -s "dist/all.c";
Eval the Entire Command (Not Just The Arguments)
eval ">"dist/support.txt" cat ${component_data_pattern}";
test -s "dist/all.c";
I don't like this, but it works. Given that we're trying to expand a pattern that includes both braces and file globs, one of which takes place before variable expansion and one of which takes place after, there may be no alternative to something like this: manually expanding the variable into a string that includes the entire command call, and using that string as the argument to eval
or bash -c
. Don't forget to escape any inner quotes with "
.
In the example above, there are no other arguments. If there are other arguments and those also use some kind of substitution, you'll need to escape those (with $
, *
, {
, or }
) so they're not expanded until the command is finally being evaluated and they can be interpreted in context.
readonly annoying_arg="$PWD/src/docs/test data.txt";
eval ">"dist/support.txt" cat ${component_data_pattern} "$annoying_arg"";
test -s "dist/all.c";
answered Dec 19 '18 at 20:04
Jean
306
306
I got this solution working as I finished writing the question, but I don't like it very much. I would love any better suggestions that anybody might have.
– Jean
Dec 19 '18 at 20:05
add a comment |
I got this solution working as I finished writing the question, but I don't like it very much. I would love any better suggestions that anybody might have.
– Jean
Dec 19 '18 at 20:05
I got this solution working as I finished writing the question, but I don't like it very much. I would love any better suggestions that anybody might have.
– Jean
Dec 19 '18 at 20:05
I got this solution working as I finished writing the question, but I don't like it very much. I would love any better suggestions that anybody might have.
– Jean
Dec 19 '18 at 20:05
add a comment |
Thanks for contributing an answer to Unix & Linux Stack Exchange!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Some of your past answers have not been well-received, and you're in danger of being blocked from answering.
Please pay close attention to the following guidance:
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2funix.stackexchange.com%2fquestions%2f490000%2frunning-multiple-command-against-files-matching-a-braceglob-pattern-without-rep%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown