Reputation: 5911
In a bash script, I'd like to:
loglowlevel.txt
),I wrote a basic script that redirects stdout
and sterr
to FD 3. loglowlevel.txt
is correctly populated. But I am stuck in specifying the option for the high-level logs.
#!/bin/bash -
# create fd 3
exec 3<> loglowlevel.txt
# redirect stdout and stderr to fd 3
exec 1>&3
exec 2>&3
# high-level logs' redirection below is wrong
echo "high-level comment" 3>&1
# low-level logs should remain redirection-free, as below
echo "low-level comment"
ls notafile
# close fd 3
3>&-
Here is what it does:
$ redirect.sh
$ cat loglowlevel.txt
low-level comment
ls: cannot access notafile: No such file or directory
I expected high-level comment
to be printed on terminal as well.
Second script, different strategy:
#!/bin/bash -
function echolowlevel() {
echo $1 &>loglowlevel.txt
}
function echohighlevel() {
echo $1 |& tee loglowlevel.txt
}
echohighlevel "high-level comment 1"
echolowlevel "low-level comment 1"
echohighlevel "high-level comment 2"
ls notafile
Here is what it does:
$ redirect.sh
high-level comment 1
high-level comment 2
ls: cannot access notafile: No such file or directory
$ cat loglowlevel.txt
high-level comment 2
Two problems here:
ls
is printed in terminal, whereas I need it only in loglowlevel.txt
.high-level comment 1
has been eaten in loglowlevel.txt
.I prefer the idea behind Take 1. But how can I make high-level comment
be output to stdout while keeping the two exec
commands?
Upvotes: 2
Views: 600
Reputation: 532508
A shorter version of William Pursell's answer, for shells and operating systems that support process substitution:
exec 3>> >(tee -a loglowlevel.txt)
exec >> loglowlevel.txt
echo high-level >&3 # Appears on original stdout and in loglowlevel.txt
echo low-level # Appears only in loglowlevel.txt
In this example, writing to file descriptor 3 effectively writes to the standard input of a background tee
process that appends to the file "loglowlevel.txt".
Support for this feature varies. It's not part of the POSIX standard, but it is provided by at least bash
, ksh
, and zsh
. Each shell will require some amount of operating system support. The bash
version, for example, requires the availability of named pipes (the object created by mkfifo
in William's solution) or access to open files via /dev/fd
.
Upvotes: 1
Reputation: 212674
#!/bin/sh
FIFO=/tmp/fifo.$$ # or use tmpfile, or some other mechanism to get unique name
trap 'rm -f $FIFO' 0
mkfifo $FIFO
tee -a loglowlevel.txt < $FIFO &
exec >> loglowlevel.txt
exec 3> $FIFO
echo high-level >&3 # Appears on original stdout and in loglowlevel.txt
echo low-level # Appears only in loglowlevel.txt
Upvotes: 3