How can I generate arguments to another command via command substitution
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
add a comment |
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
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 maybeevalcould be used, but it's generally not recommended.xargsis something to consider as well
– Sergiy Kolodyazhnyy
Jan 21 at 11:44
I'd like (expect) the output fromsomecommandto 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
add a comment |
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
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
shell arguments command-substitution
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 maybeevalcould be used, but it's generally not recommended.xargsis something to consider as well
– Sergiy Kolodyazhnyy
Jan 21 at 11:44
I'd like (expect) the output fromsomecommandto 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
add a comment |
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 maybeevalcould be used, but it's generally not recommended.xargsis something to consider as well
– Sergiy Kolodyazhnyy
Jan 21 at 11:44
I'd like (expect) the output fromsomecommandto 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
add a comment |
3 Answers
3
active
oldest
votes
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.)
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
add a comment |
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).
could you explainset --?
– user1207217
Jan 21 at 11:58
@user1207217 Added explanation to answer.
– Kusalananda
Jan 21 at 12:01
add a comment |
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.
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%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
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.)
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
add a comment |
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.)
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
add a comment |
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.)
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.)
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
add a comment |
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
add a comment |
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).
could you explainset --?
– user1207217
Jan 21 at 11:58
@user1207217 Added explanation to answer.
– Kusalananda
Jan 21 at 12:01
add a comment |
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).
could you explainset --?
– user1207217
Jan 21 at 11:58
@user1207217 Added explanation to answer.
– Kusalananda
Jan 21 at 12:01
add a comment |
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).
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).
edited Jan 21 at 18:49
answered Jan 21 at 11:50
KusalanandaKusalananda
132k17250411
132k17250411
could you explainset --?
– user1207217
Jan 21 at 11:58
@user1207217 Added explanation to answer.
– Kusalananda
Jan 21 at 12:01
add a comment |
could you explainset --?
– 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
add a comment |
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.
add a comment |
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.
add a comment |
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.
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.
answered Jan 21 at 11:54
OlorinOlorin
3,4431418
3,4431418
add a comment |
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.
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%2f495769%2fhow-can-i-generate-arguments-to-another-command-via-command-substitution%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
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
evalcould be used, but it's generally not recommended.xargsis something to consider as well– Sergiy Kolodyazhnyy
Jan 21 at 11:44
I'd like (expect) the output from
somecommandto 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