2

In a bash script, I'd like to:

  1. output every logs into a file (loglowlevel.txt),
  2. but make only few of them visible in terminal (high-level),
  3. I have, say, 20 high-level logs and 60 low-level logs. Thus I'd like to keep low-level logs "redirection-command" free, and have the redirection stuff to high-level logs only.

Take 1

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.

Take 2

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:

  1. error message from ls is printed in terminal, whereas I need it only in loglowlevel.txt.
  2. high-level comment 1 has been eaten in loglowlevel.txt.

Question

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?

3
  • Do you know about tee(1)?
    – Carl Norum
    Commented Feb 11, 2013 at 18:06
  • Output redirection takes output from one file descriptor to another, i.e, it's a one-to-one mapping. You can't have output go to two separate sources with redirection alone.
    – chepner
    Commented Feb 11, 2013 at 18:10
  • @CarlNorum I didn't know about -a option in tee.
    – m-ric
    Commented Feb 11, 2013 at 18:44

2 Answers 2

3
#!/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
6
  • If process substitution is available, you need just two lines (if I got it right): exec 3>> >(tee -a loglowlevel); exec >> loglowlevel.txt.
    – chepner
    Commented Feb 11, 2013 at 18:16
  • @William that works, thanks. @chepner exec 3>> >(tee -a loglowlevel.txt); exec >> loglowlevel.txt line 4: syntax error near unexpected token '>' line 4: 'exec 3>> >(tee -a loglowlevel)'
    – m-ric
    Commented Feb 11, 2013 at 18:53
  • m-ric: You appear not to have process substitution, then. Are you using the /bin/sh shebang in William's script? If so, change it to #!/bin/bash.
    – chepner
    Commented Feb 11, 2013 at 18:59
  • @chepner you're right! dash complains. With #!/bin/bash, your solution works. You should post it as an answer! Can you describe how exec 3>> > works, I have not seen this syntax in the literature.
    – m-ric
    Commented Feb 11, 2013 at 19:06
  • @m-ric A description of process substitution is available in the bash manpage. It is a highly non-portable construct. If used, it will effectively tie your script to bash and make it difficult to use any other shell. Commented Feb 11, 2013 at 19:11
1

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.

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