How can I make Bash interpret the contents of开发者_开发百科 a variable as I/O redirects and not simply pass those contents to the command being executed. Take this script for example:
#!/bin/bash
test "$1" == '--log' && LOGGING="2>&1 | tee time.log" || LOGGING=""
date $LOGGING
The desired behavior is that when I run this script with the --log option bash wil execute
$ date 2>&1 | tee time.log
If I don't specify --log then it simply outputs date without creating a log. Instead it passes the contents of $LOGGING to date as a CLI argument resulting in an error:
date: extra operand `|' Try `date --help' for more information.
Is there way to do this without writing something like
#!/bin/bash
test "$1" == '--log' && date 2>&1 | tee time.log || date
The actual application is obviously much more complicated than just calling "date" so I'd like to avoid copying and pasting that command twice in an if else just to append the redirect and logging commands.
If your script is rather long and you want to log all stdout and stderr when --log is passed in, I suggest using exec to redirect everything. See this excellent article:
http://www.linuxjournal.com/content/bash-redirections-using-exec
#!/bin/bash
if [[ "$1" == '--log' ]]; then
npipe=/tmp/$$.tmp
trap "rm -f $npipe" EXIT
mknod $npipe p
tee <$npipe log &
exec 1>&-
exec 1>$npipe
fi
date
# and any other commands after this will be logged too.
The interesting thing about this approach is that you can also prepend all logged lines with a timestamp using perl or gawk or some other utility:
#!/bin/bash
if [[ "$1" == '--log' ]]; then
npipe=/tmp/$$.tmp
trap "rm -f $npipe" EXIT
mknod $npipe p
perl -pne 'print scalar(localtime()), " ";' < $npipe | tee time.log &
exec 1>&-
exec 1>$npipe 2>&1
fi
echo hello world
echo hello world 2
After running this, time.log will contain:
$ cat time.log
Wed Jun 8 13:28:45 2011 hello world
Wed Jun 8 13:28:45 2011 hello world 2
The drawback here is that the timestamp are printed to your terminal too.
You can use eval
:
eval date $LOGGING
The problem is that by putting the command in a variable, you are effectively converting everything to strings instead of leaving it as Bash keywords. Try appending -x
to the shebang line:
$ ./test.sh --log
+ test --log == --log
+ LOGGING='2>&1 | tee time.log'
+ date '2>&1' '|' tee time.log
date: extra operand `|'
Try `date --help' for more information.
Try this:
#!/bin/bash -x
logging() {
if [ "$1" == '--log' ]
then
cat 2>&1 | tee time.log
else
cat
fi
}
date | logging "$1"
精彩评论