7

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?

6
  • 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
    CommentedOct 15, 2015 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
    CommentedOct 15, 2015 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
    CommentedOct 15, 2015 at 15:47
  • Is $IS_VERBOSE a command? You are treating it as one.
    – terdon
    CommentedOct 15, 2015 at 15:48
  • $IS_VERBOSE is a variable. I will edit to show how it's set.
    – asmodean
    CommentedOct 15, 2015 at 15:49

3 Answers 3

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 
1
  • 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
    CommentedOct 15, 2015 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.

1
  • 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
    CommentedOct 15, 2015 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

1
  • 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
    CommentedOct 15, 2015 at 20:27

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.