Running multiple command against files matching a brace+glob pattern without repeating it












1














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
[...]









share|improve this question





























    1














    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
    [...]









    share|improve this question



























      1












      1








      1







      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
      [...]









      share|improve this question















      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






      share|improve this question















      share|improve this question













      share|improve this question




      share|improve this question








      edited Dec 20 '18 at 19:33

























      asked Dec 19 '18 at 20:04









      Jean

      306




      306






















          2 Answers
          2






          active

          oldest

          votes


















          0














          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.






          share|improve this answer























          • 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





















          1














          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";





          share|improve this answer





















          • 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











          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
          });


          }
          });














          draft saved

          draft discarded


















          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









          0














          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.






          share|improve this answer























          • 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


















          0














          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.






          share|improve this answer























          • 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
















          0












          0








          0






          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.






          share|improve this answer














          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.







          share|improve this answer














          share|improve this answer



          share|improve this answer








          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




















          • 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















          1














          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";





          share|improve this answer





















          • 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
















          1














          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";





          share|improve this answer





















          • 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














          1












          1








          1






          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";





          share|improve this answer












          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";






          share|improve this answer












          share|improve this answer



          share|improve this answer










          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


















          • 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


















          draft saved

          draft discarded




















































          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.




          draft saved


          draft discarded














          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





















































          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







          Popular posts from this blog

          Morgemoulin

          Scott Moir

          Souastre