How can I generate arguments to another command via command substitution












10















Following on from:
unexpected behaviour in shell command substitution



I have a command which can take a huge list of arguments, some of which can legitimately contain spaces (and probably other things)



I wrote a script which can generate those arguments for me, with quotes, but I must copy and paste the output e.g.



./somecommand
<output on stdout with quoting>
./othercommand some_args <output from above>


I tried to streamline this by simply doing



./othercommand $(./somecommand)


and ran into the unexpected behaviour mentioned in question above. The question is -- can command substitution be reliably used to generate the arguments to othercommand given that some arguments require quoting and this cannot be changed?










share|improve this question

























  • Depends on what you mean by "reliably". If you want next command take the output exactly as it appears on screen and apply shell rules to it, then maybe eval could be used, but it's generally not recommended. xargs is something to consider as well

    – Sergiy Kolodyazhnyy
    Jan 21 at 11:44











  • I'd like (expect) the output from somecommand to undergo regular shell parsing

    – user1207217
    Jan 21 at 11:45











  • As I said in my answer, use some other character for field splitting (like :)... assuming that character will reliably not be in the output.

    – Olorin
    Jan 21 at 11:45













  • But that's not really right because it doesn't obey quoting rules, which is what this is about

    – user1207217
    Jan 21 at 11:47






  • 2





    Could you post a real-world example? I mean, the actual output of the first command and how you want it to interact with the second command.

    – nxnev
    Jan 21 at 11:49
















10















Following on from:
unexpected behaviour in shell command substitution



I have a command which can take a huge list of arguments, some of which can legitimately contain spaces (and probably other things)



I wrote a script which can generate those arguments for me, with quotes, but I must copy and paste the output e.g.



./somecommand
<output on stdout with quoting>
./othercommand some_args <output from above>


I tried to streamline this by simply doing



./othercommand $(./somecommand)


and ran into the unexpected behaviour mentioned in question above. The question is -- can command substitution be reliably used to generate the arguments to othercommand given that some arguments require quoting and this cannot be changed?










share|improve this question

























  • Depends on what you mean by "reliably". If you want next command take the output exactly as it appears on screen and apply shell rules to it, then maybe eval could be used, but it's generally not recommended. xargs is something to consider as well

    – Sergiy Kolodyazhnyy
    Jan 21 at 11:44











  • I'd like (expect) the output from somecommand to undergo regular shell parsing

    – user1207217
    Jan 21 at 11:45











  • As I said in my answer, use some other character for field splitting (like :)... assuming that character will reliably not be in the output.

    – Olorin
    Jan 21 at 11:45













  • But that's not really right because it doesn't obey quoting rules, which is what this is about

    – user1207217
    Jan 21 at 11:47






  • 2





    Could you post a real-world example? I mean, the actual output of the first command and how you want it to interact with the second command.

    – nxnev
    Jan 21 at 11:49














10












10








10


2






Following on from:
unexpected behaviour in shell command substitution



I have a command which can take a huge list of arguments, some of which can legitimately contain spaces (and probably other things)



I wrote a script which can generate those arguments for me, with quotes, but I must copy and paste the output e.g.



./somecommand
<output on stdout with quoting>
./othercommand some_args <output from above>


I tried to streamline this by simply doing



./othercommand $(./somecommand)


and ran into the unexpected behaviour mentioned in question above. The question is -- can command substitution be reliably used to generate the arguments to othercommand given that some arguments require quoting and this cannot be changed?










share|improve this question
















Following on from:
unexpected behaviour in shell command substitution



I have a command which can take a huge list of arguments, some of which can legitimately contain spaces (and probably other things)



I wrote a script which can generate those arguments for me, with quotes, but I must copy and paste the output e.g.



./somecommand
<output on stdout with quoting>
./othercommand some_args <output from above>


I tried to streamline this by simply doing



./othercommand $(./somecommand)


and ran into the unexpected behaviour mentioned in question above. The question is -- can command substitution be reliably used to generate the arguments to othercommand given that some arguments require quoting and this cannot be changed?







shell arguments command-substitution






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Jan 22 at 0:45









Jeff Schaller

42.1k1156133




42.1k1156133










asked Jan 21 at 11:40









user1207217user1207217

60849




60849













  • Depends on what you mean by "reliably". If you want next command take the output exactly as it appears on screen and apply shell rules to it, then maybe eval could be used, but it's generally not recommended. xargs is something to consider as well

    – Sergiy Kolodyazhnyy
    Jan 21 at 11:44











  • I'd like (expect) the output from somecommand to undergo regular shell parsing

    – user1207217
    Jan 21 at 11:45











  • As I said in my answer, use some other character for field splitting (like :)... assuming that character will reliably not be in the output.

    – Olorin
    Jan 21 at 11:45













  • But that's not really right because it doesn't obey quoting rules, which is what this is about

    – user1207217
    Jan 21 at 11:47






  • 2





    Could you post a real-world example? I mean, the actual output of the first command and how you want it to interact with the second command.

    – nxnev
    Jan 21 at 11:49



















  • Depends on what you mean by "reliably". If you want next command take the output exactly as it appears on screen and apply shell rules to it, then maybe eval could be used, but it's generally not recommended. xargs is something to consider as well

    – Sergiy Kolodyazhnyy
    Jan 21 at 11:44











  • I'd like (expect) the output from somecommand to undergo regular shell parsing

    – user1207217
    Jan 21 at 11:45











  • As I said in my answer, use some other character for field splitting (like :)... assuming that character will reliably not be in the output.

    – Olorin
    Jan 21 at 11:45













  • But that's not really right because it doesn't obey quoting rules, which is what this is about

    – user1207217
    Jan 21 at 11:47






  • 2





    Could you post a real-world example? I mean, the actual output of the first command and how you want it to interact with the second command.

    – nxnev
    Jan 21 at 11:49

















Depends on what you mean by "reliably". If you want next command take the output exactly as it appears on screen and apply shell rules to it, then maybe eval could be used, but it's generally not recommended. xargs is something to consider as well

– Sergiy Kolodyazhnyy
Jan 21 at 11:44





Depends on what you mean by "reliably". If you want next command take the output exactly as it appears on screen and apply shell rules to it, then maybe eval could be used, but it's generally not recommended. xargs is something to consider as well

– Sergiy Kolodyazhnyy
Jan 21 at 11:44













I'd like (expect) the output from somecommand to undergo regular shell parsing

– user1207217
Jan 21 at 11:45





I'd like (expect) the output from somecommand to undergo regular shell parsing

– user1207217
Jan 21 at 11:45













As I said in my answer, use some other character for field splitting (like :)... assuming that character will reliably not be in the output.

– Olorin
Jan 21 at 11:45







As I said in my answer, use some other character for field splitting (like :)... assuming that character will reliably not be in the output.

– Olorin
Jan 21 at 11:45















But that's not really right because it doesn't obey quoting rules, which is what this is about

– user1207217
Jan 21 at 11:47





But that's not really right because it doesn't obey quoting rules, which is what this is about

– user1207217
Jan 21 at 11:47




2




2





Could you post a real-world example? I mean, the actual output of the first command and how you want it to interact with the second command.

– nxnev
Jan 21 at 11:49





Could you post a real-world example? I mean, the actual output of the first command and how you want it to interact with the second command.

– nxnev
Jan 21 at 11:49










3 Answers
3






active

oldest

votes


















10















I wrote a script which can generate those arguments for me, with quotes




If the output is properly quoted for the shell, and you trust the output, then you could run eval on it.



Assuming you have a shell that supports arrays, it would be best to use one to store the arguments you get.



If ./gen_args.sh produces output like 'foo bar' '*' asdf, then we could run eval "args=( $(./gen_args.sh) )" to populate an array called args with the results. That would be the three elements foo bar, *, asdf.



We can use "${args[@]}" as usual to expand the array elements individually:



$ eval "args=( $(./gen_args.sh) )"
$ for var in "${args[@]}"; do printf ":%s:n" "$var"; done
:foo bar:
:*:
:asdf:


(Note the quotes. "${array[@]}" expands to all elements as distinct arguments unmodified. Without quotes the array elements are subject to word splitting. See e.g. the Arrays page on BashGuide.)



However, eval will happily run any shell substitutions, so $HOME in the output would expand to your home directory, and a command substitution would actually run a command in the shell running eval. An output of "$(date >&2)" would create a single empty array element and print the current date on stdout. This is a concern if gen_args.sh gets the data from some untrusted source, like another host over the network, file names created by other users. The output could include arbitrary commands. (If get_args.sh itself was malicious, it wouldn't need to output anything, it could just run the malicious commands directly.)





An alternative to shell quoting, which is hard to parse without eval, would be to use some other character as separator in the output of your script. You'd need to pick one that is not needed in the actual arguments.



Let's choose #, and have the script output foo bar#*#asdf. Now we can use unquoted command expansion to split the output of the command to the arguments.



$ IFS='#'                          # split on '#' signs
$ set -f # disable globbing
$ args=( $( ./gen_args3.sh ) ) # assign the values to the array
$ for var in "${args[@]}"; do printf ":%s:n" "$var"; done
:foo bar:
:*:
:asdf:


You'll need to set IFS back later if you depend on word splitting elsewhere in the script (unset IFS should work to make it the default), and also use set +f if you want to use globbing later.



If you're not using Bash or some other shell that has arrays, you could use the positional parameters for that. Replace args=( $(...) ) with set -- $(./gen_args.sh) and use "$@" instead of "${args[@]}" then. (Here, too, you need quotes around "$@", otherwise the positional parameters are subject to word splitting.)






share|improve this answer


























  • Best of both worlds!

    – Olorin
    Jan 21 at 12:20











  • Would you add a remark showing the importance of quoting ${args[@]} -- this didn't work for me otherwise

    – user1207217
    Jan 21 at 15:10











  • @user1207217, yes, you're right. It's the same thing with arrays and "${array[@]}" as with "$@". Both need to be quoted, or otherwise word splitting breaks the array elements to parts.

    – ilkkachu
    Jan 21 at 20:11



















6














The issue is that once your somecommand script outputs the options for othercommand, the options are really just text and at the mercy of the shell's standard parsing (affected by whatever $IFS happens to be and what shell options are in effect, which you in the general case would not be in control over).



Instead of using somecommand to output the options, it would be easier, safer, and more robust to use it to call othercommand. The somecommand script would then be a wrapper script around othercommand instead of some sort of helper script that you would have to remember to call in some special way as part of the command line of otherscript. Wrapper scripts are a very common way of providing a tool that just calls some other similar tool with another set of options (just check with file what commands in /usr/bin are actually shell script wrappers).



In bash, ksh or zsh, you could easily a wrapper script that uses an array to hold the individual options for othercommand like so:



options=( "hi there" "nice weather" "here's a star" "*" )
options+=( "bonus bumblebee!" ) # add additional option


Then call othercommand (still within the wrapper script):



othercommand "${options[@]}"


The expansion of "${options[@]}" would ensure that each element of the options array is individually quoted and presented to othercommand as separate arguments.



The user of the wrapper would be oblivious to the fact that it's actually calling othercommand, something that would not be true if the script instead just generated the command line options for othercommand as output.



In /bin/sh, use $@ to hold the options:



set -- "hi there" "nice weather" "here's a star" "*"
set -- "$@" "bonus bumblebee!" # add additional option

othercommand "$@"


(set is the command used for setting the positional parameters $1, $2, $3 etc. These are what makes up the array $@ in a standard POSIX shell. The initial -- is to signal to set that there are no options given, only arguments. The -- is really only needed if the first value happens to be something starting with -).



Note that it's the double quotes around $@ and ${options[@]} that ensures that the elements are not individually word-splitted (and filename globbed).






share|improve this answer


























  • could you explain set -- ?

    – user1207217
    Jan 21 at 11:58











  • @user1207217 Added explanation to answer.

    – Kusalananda
    Jan 21 at 12:01



















4














If the somecommand output is in reliably good shell syntax, you can use eval:



$ eval sh test.sh $(echo '"hello " "hi and bye"')
hello
hi and bye


But you have to be sure that the output has valid quoting and such, otherwise you might end up running commands outside the script as well:



$ cat test.sh 
for var in "$@"
do
echo "|$var|"
done
$ ls
bar baz test.sh
$ eval sh test.sh $(echo '"hello " "hi and bye"; echo rm *')
|hello |
|hi and bye|
rm bar baz test.sh


Note that echo rm bar baz test.sh wasn't passed to the script (because of the ;) and was run as a separate command. I added the | around $var to highlight this.





Generally, unless you can completely trust the output of somecommand, it's not possible to reliably use its output to build a command string.






share|improve this answer























    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%2f495769%2fhow-can-i-generate-arguments-to-another-command-via-command-substitution%23new-answer', 'question_page');
    }
    );

    Post as a guest















    Required, but never shown

























    3 Answers
    3






    active

    oldest

    votes








    3 Answers
    3






    active

    oldest

    votes









    active

    oldest

    votes






    active

    oldest

    votes









    10















    I wrote a script which can generate those arguments for me, with quotes




    If the output is properly quoted for the shell, and you trust the output, then you could run eval on it.



    Assuming you have a shell that supports arrays, it would be best to use one to store the arguments you get.



    If ./gen_args.sh produces output like 'foo bar' '*' asdf, then we could run eval "args=( $(./gen_args.sh) )" to populate an array called args with the results. That would be the three elements foo bar, *, asdf.



    We can use "${args[@]}" as usual to expand the array elements individually:



    $ eval "args=( $(./gen_args.sh) )"
    $ for var in "${args[@]}"; do printf ":%s:n" "$var"; done
    :foo bar:
    :*:
    :asdf:


    (Note the quotes. "${array[@]}" expands to all elements as distinct arguments unmodified. Without quotes the array elements are subject to word splitting. See e.g. the Arrays page on BashGuide.)



    However, eval will happily run any shell substitutions, so $HOME in the output would expand to your home directory, and a command substitution would actually run a command in the shell running eval. An output of "$(date >&2)" would create a single empty array element and print the current date on stdout. This is a concern if gen_args.sh gets the data from some untrusted source, like another host over the network, file names created by other users. The output could include arbitrary commands. (If get_args.sh itself was malicious, it wouldn't need to output anything, it could just run the malicious commands directly.)





    An alternative to shell quoting, which is hard to parse without eval, would be to use some other character as separator in the output of your script. You'd need to pick one that is not needed in the actual arguments.



    Let's choose #, and have the script output foo bar#*#asdf. Now we can use unquoted command expansion to split the output of the command to the arguments.



    $ IFS='#'                          # split on '#' signs
    $ set -f # disable globbing
    $ args=( $( ./gen_args3.sh ) ) # assign the values to the array
    $ for var in "${args[@]}"; do printf ":%s:n" "$var"; done
    :foo bar:
    :*:
    :asdf:


    You'll need to set IFS back later if you depend on word splitting elsewhere in the script (unset IFS should work to make it the default), and also use set +f if you want to use globbing later.



    If you're not using Bash or some other shell that has arrays, you could use the positional parameters for that. Replace args=( $(...) ) with set -- $(./gen_args.sh) and use "$@" instead of "${args[@]}" then. (Here, too, you need quotes around "$@", otherwise the positional parameters are subject to word splitting.)






    share|improve this answer


























    • Best of both worlds!

      – Olorin
      Jan 21 at 12:20











    • Would you add a remark showing the importance of quoting ${args[@]} -- this didn't work for me otherwise

      – user1207217
      Jan 21 at 15:10











    • @user1207217, yes, you're right. It's the same thing with arrays and "${array[@]}" as with "$@". Both need to be quoted, or otherwise word splitting breaks the array elements to parts.

      – ilkkachu
      Jan 21 at 20:11
















    10















    I wrote a script which can generate those arguments for me, with quotes




    If the output is properly quoted for the shell, and you trust the output, then you could run eval on it.



    Assuming you have a shell that supports arrays, it would be best to use one to store the arguments you get.



    If ./gen_args.sh produces output like 'foo bar' '*' asdf, then we could run eval "args=( $(./gen_args.sh) )" to populate an array called args with the results. That would be the three elements foo bar, *, asdf.



    We can use "${args[@]}" as usual to expand the array elements individually:



    $ eval "args=( $(./gen_args.sh) )"
    $ for var in "${args[@]}"; do printf ":%s:n" "$var"; done
    :foo bar:
    :*:
    :asdf:


    (Note the quotes. "${array[@]}" expands to all elements as distinct arguments unmodified. Without quotes the array elements are subject to word splitting. See e.g. the Arrays page on BashGuide.)



    However, eval will happily run any shell substitutions, so $HOME in the output would expand to your home directory, and a command substitution would actually run a command in the shell running eval. An output of "$(date >&2)" would create a single empty array element and print the current date on stdout. This is a concern if gen_args.sh gets the data from some untrusted source, like another host over the network, file names created by other users. The output could include arbitrary commands. (If get_args.sh itself was malicious, it wouldn't need to output anything, it could just run the malicious commands directly.)





    An alternative to shell quoting, which is hard to parse without eval, would be to use some other character as separator in the output of your script. You'd need to pick one that is not needed in the actual arguments.



    Let's choose #, and have the script output foo bar#*#asdf. Now we can use unquoted command expansion to split the output of the command to the arguments.



    $ IFS='#'                          # split on '#' signs
    $ set -f # disable globbing
    $ args=( $( ./gen_args3.sh ) ) # assign the values to the array
    $ for var in "${args[@]}"; do printf ":%s:n" "$var"; done
    :foo bar:
    :*:
    :asdf:


    You'll need to set IFS back later if you depend on word splitting elsewhere in the script (unset IFS should work to make it the default), and also use set +f if you want to use globbing later.



    If you're not using Bash or some other shell that has arrays, you could use the positional parameters for that. Replace args=( $(...) ) with set -- $(./gen_args.sh) and use "$@" instead of "${args[@]}" then. (Here, too, you need quotes around "$@", otherwise the positional parameters are subject to word splitting.)






    share|improve this answer


























    • Best of both worlds!

      – Olorin
      Jan 21 at 12:20











    • Would you add a remark showing the importance of quoting ${args[@]} -- this didn't work for me otherwise

      – user1207217
      Jan 21 at 15:10











    • @user1207217, yes, you're right. It's the same thing with arrays and "${array[@]}" as with "$@". Both need to be quoted, or otherwise word splitting breaks the array elements to parts.

      – ilkkachu
      Jan 21 at 20:11














    10












    10








    10








    I wrote a script which can generate those arguments for me, with quotes




    If the output is properly quoted for the shell, and you trust the output, then you could run eval on it.



    Assuming you have a shell that supports arrays, it would be best to use one to store the arguments you get.



    If ./gen_args.sh produces output like 'foo bar' '*' asdf, then we could run eval "args=( $(./gen_args.sh) )" to populate an array called args with the results. That would be the three elements foo bar, *, asdf.



    We can use "${args[@]}" as usual to expand the array elements individually:



    $ eval "args=( $(./gen_args.sh) )"
    $ for var in "${args[@]}"; do printf ":%s:n" "$var"; done
    :foo bar:
    :*:
    :asdf:


    (Note the quotes. "${array[@]}" expands to all elements as distinct arguments unmodified. Without quotes the array elements are subject to word splitting. See e.g. the Arrays page on BashGuide.)



    However, eval will happily run any shell substitutions, so $HOME in the output would expand to your home directory, and a command substitution would actually run a command in the shell running eval. An output of "$(date >&2)" would create a single empty array element and print the current date on stdout. This is a concern if gen_args.sh gets the data from some untrusted source, like another host over the network, file names created by other users. The output could include arbitrary commands. (If get_args.sh itself was malicious, it wouldn't need to output anything, it could just run the malicious commands directly.)





    An alternative to shell quoting, which is hard to parse without eval, would be to use some other character as separator in the output of your script. You'd need to pick one that is not needed in the actual arguments.



    Let's choose #, and have the script output foo bar#*#asdf. Now we can use unquoted command expansion to split the output of the command to the arguments.



    $ IFS='#'                          # split on '#' signs
    $ set -f # disable globbing
    $ args=( $( ./gen_args3.sh ) ) # assign the values to the array
    $ for var in "${args[@]}"; do printf ":%s:n" "$var"; done
    :foo bar:
    :*:
    :asdf:


    You'll need to set IFS back later if you depend on word splitting elsewhere in the script (unset IFS should work to make it the default), and also use set +f if you want to use globbing later.



    If you're not using Bash or some other shell that has arrays, you could use the positional parameters for that. Replace args=( $(...) ) with set -- $(./gen_args.sh) and use "$@" instead of "${args[@]}" then. (Here, too, you need quotes around "$@", otherwise the positional parameters are subject to word splitting.)






    share|improve this answer
















    I wrote a script which can generate those arguments for me, with quotes




    If the output is properly quoted for the shell, and you trust the output, then you could run eval on it.



    Assuming you have a shell that supports arrays, it would be best to use one to store the arguments you get.



    If ./gen_args.sh produces output like 'foo bar' '*' asdf, then we could run eval "args=( $(./gen_args.sh) )" to populate an array called args with the results. That would be the three elements foo bar, *, asdf.



    We can use "${args[@]}" as usual to expand the array elements individually:



    $ eval "args=( $(./gen_args.sh) )"
    $ for var in "${args[@]}"; do printf ":%s:n" "$var"; done
    :foo bar:
    :*:
    :asdf:


    (Note the quotes. "${array[@]}" expands to all elements as distinct arguments unmodified. Without quotes the array elements are subject to word splitting. See e.g. the Arrays page on BashGuide.)



    However, eval will happily run any shell substitutions, so $HOME in the output would expand to your home directory, and a command substitution would actually run a command in the shell running eval. An output of "$(date >&2)" would create a single empty array element and print the current date on stdout. This is a concern if gen_args.sh gets the data from some untrusted source, like another host over the network, file names created by other users. The output could include arbitrary commands. (If get_args.sh itself was malicious, it wouldn't need to output anything, it could just run the malicious commands directly.)





    An alternative to shell quoting, which is hard to parse without eval, would be to use some other character as separator in the output of your script. You'd need to pick one that is not needed in the actual arguments.



    Let's choose #, and have the script output foo bar#*#asdf. Now we can use unquoted command expansion to split the output of the command to the arguments.



    $ IFS='#'                          # split on '#' signs
    $ set -f # disable globbing
    $ args=( $( ./gen_args3.sh ) ) # assign the values to the array
    $ for var in "${args[@]}"; do printf ":%s:n" "$var"; done
    :foo bar:
    :*:
    :asdf:


    You'll need to set IFS back later if you depend on word splitting elsewhere in the script (unset IFS should work to make it the default), and also use set +f if you want to use globbing later.



    If you're not using Bash or some other shell that has arrays, you could use the positional parameters for that. Replace args=( $(...) ) with set -- $(./gen_args.sh) and use "$@" instead of "${args[@]}" then. (Here, too, you need quotes around "$@", otherwise the positional parameters are subject to word splitting.)







    share|improve this answer














    share|improve this answer



    share|improve this answer








    edited Jan 21 at 20:10

























    answered Jan 21 at 12:11









    ilkkachuilkkachu

    59.8k895169




    59.8k895169













    • Best of both worlds!

      – Olorin
      Jan 21 at 12:20











    • Would you add a remark showing the importance of quoting ${args[@]} -- this didn't work for me otherwise

      – user1207217
      Jan 21 at 15:10











    • @user1207217, yes, you're right. It's the same thing with arrays and "${array[@]}" as with "$@". Both need to be quoted, or otherwise word splitting breaks the array elements to parts.

      – ilkkachu
      Jan 21 at 20:11



















    • Best of both worlds!

      – Olorin
      Jan 21 at 12:20











    • Would you add a remark showing the importance of quoting ${args[@]} -- this didn't work for me otherwise

      – user1207217
      Jan 21 at 15:10











    • @user1207217, yes, you're right. It's the same thing with arrays and "${array[@]}" as with "$@". Both need to be quoted, or otherwise word splitting breaks the array elements to parts.

      – ilkkachu
      Jan 21 at 20:11

















    Best of both worlds!

    – Olorin
    Jan 21 at 12:20





    Best of both worlds!

    – Olorin
    Jan 21 at 12:20













    Would you add a remark showing the importance of quoting ${args[@]} -- this didn't work for me otherwise

    – user1207217
    Jan 21 at 15:10





    Would you add a remark showing the importance of quoting ${args[@]} -- this didn't work for me otherwise

    – user1207217
    Jan 21 at 15:10













    @user1207217, yes, you're right. It's the same thing with arrays and "${array[@]}" as with "$@". Both need to be quoted, or otherwise word splitting breaks the array elements to parts.

    – ilkkachu
    Jan 21 at 20:11





    @user1207217, yes, you're right. It's the same thing with arrays and "${array[@]}" as with "$@". Both need to be quoted, or otherwise word splitting breaks the array elements to parts.

    – ilkkachu
    Jan 21 at 20:11













    6














    The issue is that once your somecommand script outputs the options for othercommand, the options are really just text and at the mercy of the shell's standard parsing (affected by whatever $IFS happens to be and what shell options are in effect, which you in the general case would not be in control over).



    Instead of using somecommand to output the options, it would be easier, safer, and more robust to use it to call othercommand. The somecommand script would then be a wrapper script around othercommand instead of some sort of helper script that you would have to remember to call in some special way as part of the command line of otherscript. Wrapper scripts are a very common way of providing a tool that just calls some other similar tool with another set of options (just check with file what commands in /usr/bin are actually shell script wrappers).



    In bash, ksh or zsh, you could easily a wrapper script that uses an array to hold the individual options for othercommand like so:



    options=( "hi there" "nice weather" "here's a star" "*" )
    options+=( "bonus bumblebee!" ) # add additional option


    Then call othercommand (still within the wrapper script):



    othercommand "${options[@]}"


    The expansion of "${options[@]}" would ensure that each element of the options array is individually quoted and presented to othercommand as separate arguments.



    The user of the wrapper would be oblivious to the fact that it's actually calling othercommand, something that would not be true if the script instead just generated the command line options for othercommand as output.



    In /bin/sh, use $@ to hold the options:



    set -- "hi there" "nice weather" "here's a star" "*"
    set -- "$@" "bonus bumblebee!" # add additional option

    othercommand "$@"


    (set is the command used for setting the positional parameters $1, $2, $3 etc. These are what makes up the array $@ in a standard POSIX shell. The initial -- is to signal to set that there are no options given, only arguments. The -- is really only needed if the first value happens to be something starting with -).



    Note that it's the double quotes around $@ and ${options[@]} that ensures that the elements are not individually word-splitted (and filename globbed).






    share|improve this answer


























    • could you explain set -- ?

      – user1207217
      Jan 21 at 11:58











    • @user1207217 Added explanation to answer.

      – Kusalananda
      Jan 21 at 12:01
















    6














    The issue is that once your somecommand script outputs the options for othercommand, the options are really just text and at the mercy of the shell's standard parsing (affected by whatever $IFS happens to be and what shell options are in effect, which you in the general case would not be in control over).



    Instead of using somecommand to output the options, it would be easier, safer, and more robust to use it to call othercommand. The somecommand script would then be a wrapper script around othercommand instead of some sort of helper script that you would have to remember to call in some special way as part of the command line of otherscript. Wrapper scripts are a very common way of providing a tool that just calls some other similar tool with another set of options (just check with file what commands in /usr/bin are actually shell script wrappers).



    In bash, ksh or zsh, you could easily a wrapper script that uses an array to hold the individual options for othercommand like so:



    options=( "hi there" "nice weather" "here's a star" "*" )
    options+=( "bonus bumblebee!" ) # add additional option


    Then call othercommand (still within the wrapper script):



    othercommand "${options[@]}"


    The expansion of "${options[@]}" would ensure that each element of the options array is individually quoted and presented to othercommand as separate arguments.



    The user of the wrapper would be oblivious to the fact that it's actually calling othercommand, something that would not be true if the script instead just generated the command line options for othercommand as output.



    In /bin/sh, use $@ to hold the options:



    set -- "hi there" "nice weather" "here's a star" "*"
    set -- "$@" "bonus bumblebee!" # add additional option

    othercommand "$@"


    (set is the command used for setting the positional parameters $1, $2, $3 etc. These are what makes up the array $@ in a standard POSIX shell. The initial -- is to signal to set that there are no options given, only arguments. The -- is really only needed if the first value happens to be something starting with -).



    Note that it's the double quotes around $@ and ${options[@]} that ensures that the elements are not individually word-splitted (and filename globbed).






    share|improve this answer


























    • could you explain set -- ?

      – user1207217
      Jan 21 at 11:58











    • @user1207217 Added explanation to answer.

      – Kusalananda
      Jan 21 at 12:01














    6












    6








    6







    The issue is that once your somecommand script outputs the options for othercommand, the options are really just text and at the mercy of the shell's standard parsing (affected by whatever $IFS happens to be and what shell options are in effect, which you in the general case would not be in control over).



    Instead of using somecommand to output the options, it would be easier, safer, and more robust to use it to call othercommand. The somecommand script would then be a wrapper script around othercommand instead of some sort of helper script that you would have to remember to call in some special way as part of the command line of otherscript. Wrapper scripts are a very common way of providing a tool that just calls some other similar tool with another set of options (just check with file what commands in /usr/bin are actually shell script wrappers).



    In bash, ksh or zsh, you could easily a wrapper script that uses an array to hold the individual options for othercommand like so:



    options=( "hi there" "nice weather" "here's a star" "*" )
    options+=( "bonus bumblebee!" ) # add additional option


    Then call othercommand (still within the wrapper script):



    othercommand "${options[@]}"


    The expansion of "${options[@]}" would ensure that each element of the options array is individually quoted and presented to othercommand as separate arguments.



    The user of the wrapper would be oblivious to the fact that it's actually calling othercommand, something that would not be true if the script instead just generated the command line options for othercommand as output.



    In /bin/sh, use $@ to hold the options:



    set -- "hi there" "nice weather" "here's a star" "*"
    set -- "$@" "bonus bumblebee!" # add additional option

    othercommand "$@"


    (set is the command used for setting the positional parameters $1, $2, $3 etc. These are what makes up the array $@ in a standard POSIX shell. The initial -- is to signal to set that there are no options given, only arguments. The -- is really only needed if the first value happens to be something starting with -).



    Note that it's the double quotes around $@ and ${options[@]} that ensures that the elements are not individually word-splitted (and filename globbed).






    share|improve this answer















    The issue is that once your somecommand script outputs the options for othercommand, the options are really just text and at the mercy of the shell's standard parsing (affected by whatever $IFS happens to be and what shell options are in effect, which you in the general case would not be in control over).



    Instead of using somecommand to output the options, it would be easier, safer, and more robust to use it to call othercommand. The somecommand script would then be a wrapper script around othercommand instead of some sort of helper script that you would have to remember to call in some special way as part of the command line of otherscript. Wrapper scripts are a very common way of providing a tool that just calls some other similar tool with another set of options (just check with file what commands in /usr/bin are actually shell script wrappers).



    In bash, ksh or zsh, you could easily a wrapper script that uses an array to hold the individual options for othercommand like so:



    options=( "hi there" "nice weather" "here's a star" "*" )
    options+=( "bonus bumblebee!" ) # add additional option


    Then call othercommand (still within the wrapper script):



    othercommand "${options[@]}"


    The expansion of "${options[@]}" would ensure that each element of the options array is individually quoted and presented to othercommand as separate arguments.



    The user of the wrapper would be oblivious to the fact that it's actually calling othercommand, something that would not be true if the script instead just generated the command line options for othercommand as output.



    In /bin/sh, use $@ to hold the options:



    set -- "hi there" "nice weather" "here's a star" "*"
    set -- "$@" "bonus bumblebee!" # add additional option

    othercommand "$@"


    (set is the command used for setting the positional parameters $1, $2, $3 etc. These are what makes up the array $@ in a standard POSIX shell. The initial -- is to signal to set that there are no options given, only arguments. The -- is really only needed if the first value happens to be something starting with -).



    Note that it's the double quotes around $@ and ${options[@]} that ensures that the elements are not individually word-splitted (and filename globbed).







    share|improve this answer














    share|improve this answer



    share|improve this answer








    edited Jan 21 at 18:49

























    answered Jan 21 at 11:50









    KusalanandaKusalananda

    132k17250411




    132k17250411













    • could you explain set -- ?

      – user1207217
      Jan 21 at 11:58











    • @user1207217 Added explanation to answer.

      – Kusalananda
      Jan 21 at 12:01



















    • could you explain set -- ?

      – user1207217
      Jan 21 at 11:58











    • @user1207217 Added explanation to answer.

      – Kusalananda
      Jan 21 at 12:01

















    could you explain set -- ?

    – user1207217
    Jan 21 at 11:58





    could you explain set -- ?

    – user1207217
    Jan 21 at 11:58













    @user1207217 Added explanation to answer.

    – Kusalananda
    Jan 21 at 12:01





    @user1207217 Added explanation to answer.

    – Kusalananda
    Jan 21 at 12:01











    4














    If the somecommand output is in reliably good shell syntax, you can use eval:



    $ eval sh test.sh $(echo '"hello " "hi and bye"')
    hello
    hi and bye


    But you have to be sure that the output has valid quoting and such, otherwise you might end up running commands outside the script as well:



    $ cat test.sh 
    for var in "$@"
    do
    echo "|$var|"
    done
    $ ls
    bar baz test.sh
    $ eval sh test.sh $(echo '"hello " "hi and bye"; echo rm *')
    |hello |
    |hi and bye|
    rm bar baz test.sh


    Note that echo rm bar baz test.sh wasn't passed to the script (because of the ;) and was run as a separate command. I added the | around $var to highlight this.





    Generally, unless you can completely trust the output of somecommand, it's not possible to reliably use its output to build a command string.






    share|improve this answer




























      4














      If the somecommand output is in reliably good shell syntax, you can use eval:



      $ eval sh test.sh $(echo '"hello " "hi and bye"')
      hello
      hi and bye


      But you have to be sure that the output has valid quoting and such, otherwise you might end up running commands outside the script as well:



      $ cat test.sh 
      for var in "$@"
      do
      echo "|$var|"
      done
      $ ls
      bar baz test.sh
      $ eval sh test.sh $(echo '"hello " "hi and bye"; echo rm *')
      |hello |
      |hi and bye|
      rm bar baz test.sh


      Note that echo rm bar baz test.sh wasn't passed to the script (because of the ;) and was run as a separate command. I added the | around $var to highlight this.





      Generally, unless you can completely trust the output of somecommand, it's not possible to reliably use its output to build a command string.






      share|improve this answer


























        4












        4








        4







        If the somecommand output is in reliably good shell syntax, you can use eval:



        $ eval sh test.sh $(echo '"hello " "hi and bye"')
        hello
        hi and bye


        But you have to be sure that the output has valid quoting and such, otherwise you might end up running commands outside the script as well:



        $ cat test.sh 
        for var in "$@"
        do
        echo "|$var|"
        done
        $ ls
        bar baz test.sh
        $ eval sh test.sh $(echo '"hello " "hi and bye"; echo rm *')
        |hello |
        |hi and bye|
        rm bar baz test.sh


        Note that echo rm bar baz test.sh wasn't passed to the script (because of the ;) and was run as a separate command. I added the | around $var to highlight this.





        Generally, unless you can completely trust the output of somecommand, it's not possible to reliably use its output to build a command string.






        share|improve this answer













        If the somecommand output is in reliably good shell syntax, you can use eval:



        $ eval sh test.sh $(echo '"hello " "hi and bye"')
        hello
        hi and bye


        But you have to be sure that the output has valid quoting and such, otherwise you might end up running commands outside the script as well:



        $ cat test.sh 
        for var in "$@"
        do
        echo "|$var|"
        done
        $ ls
        bar baz test.sh
        $ eval sh test.sh $(echo '"hello " "hi and bye"; echo rm *')
        |hello |
        |hi and bye|
        rm bar baz test.sh


        Note that echo rm bar baz test.sh wasn't passed to the script (because of the ;) and was run as a separate command. I added the | around $var to highlight this.





        Generally, unless you can completely trust the output of somecommand, it's not possible to reliably use its output to build a command string.







        share|improve this answer












        share|improve this answer



        share|improve this answer










        answered Jan 21 at 11:54









        OlorinOlorin

        3,4431418




        3,4431418






























            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.




            draft saved


            draft discarded














            StackExchange.ready(
            function () {
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2funix.stackexchange.com%2fquestions%2f495769%2fhow-can-i-generate-arguments-to-another-command-via-command-substitution%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

            Mario Kart Wii

            Partial Derivative Guidance.

            Understanding the size os this class of aleatory events