6

I am trying to use command substitution in a bash script to output redirection symbols based on a variable like so:

IS_VERBOSE=false
curl $BLAH $( $IS_VERBOSE && echo '-i' || echo '> /dev/null' )

That is, if verbose, add the -i switch, otherwise, throw everything from stdout away. The problem is that when IS_VERBOSE is false, my command becomes

curl $BLAH \> /dev/null

More generally, command substitution escapes the characters > >> & | # $ and possibly others. How can I output these symbols without escaping using command substitution?

  • I suppose all the commands are being run in a script and not an interactive shell, right? Also, what shell are you using? Is that bash or something else? – terdon Oct 15 '15 at 15:37
  • The commands are being run in a script. I am using GNU bash, version 3.2.57(1)-release (x86_64-apple-darwin14). I am seeing the same behavior at an interactive zsh, that is $(echo ">")<tab> is replaced with \>. – asmodean Oct 15 '15 at 15:43
  • you may need to re-evaluate your expression to perform the redirection. i.e. eval curl $BLAH $( $IS_VERBOSE && echo '-i' || echo '> /dev/null' ) – adonis Oct 15 '15 at 15:47
  • Is $IS_VERBOSE a command? You are treating it as one. – terdon Oct 15 '15 at 15:48
  • $IS_VERBOSE is a variable. I will edit to show how it's set. – asmodean Oct 15 '15 at 15:49
5

After the substition happens (which BTW in POSIX could only target the left side before any ">") there is no more evaluation on whether there is any ">" so the approach you envisioned wouldn't work.

If you don't care about POSIX-conformity (after all you tagged this as 'bash') you could still find a solution by dynamically setting the right side but I would personally go for a totally different approach; have a look at the following post detailing a verbose/silent mode based on custom file descriptors: https://stackoverflow.com/a/20942015/2261442.

A code excerpt from that post to show how nice it would then look like:

# Some confirmations:
printf "%s\n" "This message is seen at verbosity level 3 and above." >&3
printf "%s\n" "This message is seen at verbosity level 4 and above." >&4
printf "%s\n" "This message is seen at verbosity level 5 and above." >&5
  • This is beautiful! I've always wondered how scripts handled multiple verbosity levels; I'm definitely going to start working this into future scripts. Also, you've answered the question with "there is no more evaluation" on redirects after substitution, so even if I could get it output "unescaped", it wouldn't be evaluated as I expect. Thanks! – asmodean Oct 15 '15 at 16:44
3

I know this isn't solving your escaping problem (which I'm not sure yet is possible), but coming from man curl, this would achieve what you're trying to do:

BLAH=localhost

IS_VERBOSE=true
OPTIONS="-o /dev/null"
$IS_VERBOSE && OPTIONS=""

curl $BLAH -s $OPTIONS
    <html>
    <p>hi</p>
    </html>

IS_VERBOSE=false
OPTIONS="-o /dev/null"
$IS_VERBOSE && OPTIONS="-i"

curl $BLAH -s $OPTIONS

The latter outputs nothing. And is less dependent on different shells (bash, sh, etc), so more portable.

  • Good catch. I'd forgotten about the -o switch since a redirect is the more ubiquitous approach to saving output to a file. This is the solution I'm going with for now. – asmodean Oct 15 '15 at 16:41
2

according to bash manual the order of shell expansions is brace expansion, tilde expansion, parameter and variable expansion, command substitution, arithmetic expansion, word splitting, and pathname expansion. also it is stated that the redirections happen before the command is executes - i.e. after shell expansion. Thus it comes to reason that commands like:

redir='>'
echo value $redir file

and

echo value $(echo '>') file

would put value in the file file.

Unfortunately this is not the case because the command line is firstly parsed and the redirection tokens are only identified at this stage. Later, before the command is executed, the redirections are performed based on those tokens.

eval curl $BLAH $( $IS_VERBOSE && echo '-i' || echo '> /dev/null' )

works by re-parsing the command, thus identifies the redirections, but it also performs shell's expansion, which is a bit tricky to work with, and may lead in unexpected behavior.

note: there is special handling in bash for >$var and >&$var

  • That makes a lot of sense now. The redirect tokens generated after the command is parsed don't mean anything special. I won't try to make that mistake again. Thanks for this answer! I feel I have a better understanding of what's going on now (as well as an obscure use-case for the evil eval comand. – asmodean Oct 15 '15 at 20:27

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.