11

In my script I want to be able to write to either a file or to stdout based on certain conditions. I'm curious as to why this doesn't work in my script:

out=\&1
echo "bird" 1>$out

I tried different combination of quotes, but I keep having a "&1" file created instead of it writing to stdout. What can I do to get this to work how I want?

Milo
  • 111
  • 1
  • 3

5 Answers5

5

A possibly safer alternative to eval is to dup your destination into a temporary file descriptor using exec (file descriptor 3 in this example):

if somecondition; then exec 3> destfile; else exec 3>&1; fi

echo bird >&3
Sean
  • 29,130
  • 4
  • 80
  • 105
  • 3
    @Dennis: I really can't understand that eval cliché... Seems to be filling all the bash-related questions in stackoverflow :) Again and the thousandth time: `eval` is a tool and not a risk by itself... It is like saying you have to avoid Lisp because you can consider Lisp data as code... – Diego Sevilla Nov 05 '10 at 18:43
  • 1
    @Diego: `eval` can be a useful tool. It also presents serious potential security risks. If you avoid it, you don't have to worry about that particular set of risks. It's often easy to avoid. Take the easy and safe route whenever possible. When you do use `eval` make sure that you really need it and that you properly sanitize the input and take the other necessary steps to prevent problems. See [BashFAQ/048](http://mywiki.wooledge.org/BashFAQ/048) and [this excellent SO question](http://stackoverflow.com/q/2571401) (for and against). Also, note that I said "best to avoid" not "don't ever use". – Dennis Williamson Nov 05 '10 at 19:14
  • 2
    @Dennis: I agree with most of what you say about "general" eval usage. But, in this case, he is controlling all the code. There is no point in trying to avoid the usage of `eval` in a code of which I have total control... – Diego Sevilla Nov 05 '10 at 21:51
  • 2
    @Diego: Why would you use eval in this case when exec is cleaner and easier to read? I believe the point @Dennis is making is that you should only use eval when there is no clear alternative. There is a better alternative in this case. – jabbie Mar 11 '11 at 23:08
  • I was having trouble getting this to work in my own script, here's the syntax I had to use for some reason: `destfile="/some/path"; exec 3>${destfile}` – Eliot May 02 '14 at 09:27
3

As of 2015, it is possible to redirect to >&${out}. E.g.,

exec {out}>&1
echo "bird" 1>&${out}
chutz
  • 2,256
  • 2
  • 25
  • 38
2

Expounding on Diego's answer. To change where stdout goes conditionally

if [ someCondition ] ; then
  # all output now goes to $file
  exec 1>$file
fi

echo "bird"

Or create your own file descriptor;

if [ someCondition ] ; then
  # 3 points to stdout
  exec 3>&1
else
  # 3 points to a file
  exec 3>$outfile
fi

echo "bird" >&3

Adapted from: csh programming considered harmful - check it out for some more redirection tricks. Or read the bash man page.

jabbie
  • 2,606
  • 1
  • 17
  • 9
  • I was having trouble getting this to work in my own script, I wound up having to use full variable expansion notation for some reason, ex: `destfile="/some/path"; exec 3>${destfile}` – Eliot May 02 '14 at 09:31
1

I'm pretty certain it has to do with the order in which bash processes the command line. The following works:

export out=\&1
eval "echo bird 1>${out}"

because the variable substitution happens before the evaluation.

paxdiablo
  • 854,327
  • 234
  • 1,573
  • 1,953
0

Try with eval. It should work by interpreting the value of $out itself:

out='&1'
eval "echo \"bird\" 1>$out"

Will print bird on the standard output (and to a file if you change out).

Note that you have to be careful with what goes inside the eval string. Note the backslash with the internal quotes, and that the variable $out is susbstituted (by means of the double quotes) before the eval is performed.

Diego Sevilla
  • 28,636
  • 4
  • 59
  • 87