Display script output on screen and log it to a file at the same time

I’ve written plenty of scripts in the past where I’ve had to choose, do I want to script to output to stdout or do I want it to log to a file? Sure, if I’m outputting to stdout I could always call the script and pipe to tee to generate a log, but if I’m writing a script for others to use maybe I don’t want to have to depend on them to do that. Or maybe I just want to be lazy and not have to remember to pipe to tee every time I run the script.

Well, I just found the greatest trick that lets you do both from within the script.

#!/bin/bash

LOGFILE=${0##*/}.out
exec > >(tee ${LOGFILE})
exec 2>&1

( do stuff that generates output to stdout or stderr )

First, the ${0##*/} is some magic that works similarly to basename $0, but is way cooler looking. It works in bash, and I’ve read it works in ksh but I haven’t tested that.

Second is the exec > >(tee ${LOGFILE}) bit. I don’t know much about it, but apparently > >( ) is functionally similar to a pipe. It takes the script’s stdout and sends it to tee. Combine that with the exec 2>&1 and you’ve got both stderr and stdout getting piped to tee. Like I said, I don’t fully understand this technique, but it sure does work pretty well.

Thanks, Naked Ape!

Update – August 19, 2011 at 10:45:
I’ve been doing research on this. The ${0##*/} bit uses a shell feature called parameter expansion in bash or parameter substitution in ksh. According to the bash manpage:

${parameter##word}
The word is expanded to produce a pattern just as in pathname expansion. If the pattern matches the beginning of the value of parameter, then the result of the expansion is the expanded value of parameter with the longest matching pattern deleted. If parameter is @ or *, the pattern removal operation is applied to each positional parameter in turn, and the expansion is the resultant list. If parameter is an array variable subscripted with @ or *, the pattern removal operation is applied to each member of the array in turn, and the expansion is the resultant list.

The exec > >(some_command) bit is called process substitution. It is supported in bash on machines that support /dev/fd or named pipes. It’s supported in ksh93 on machines that support /dev/fd.

Advertisements

2 Responses to Display script output on screen and log it to a file at the same time

  1. Voodooo says:

    This was very helpful. My scripts only worked properly with this however, when the following was added at the end of the script:

    exec 1>&- 2>&-

  2. kdknigga says:

    I’m not sure why it’s not fully working for you. What shell are you using?

    I just created a test script:

    $ cat script_that_logs.sh
    #!/bin/bash
    
    LOGFILE=/tmp/${0##*/}.out
    
    exec > >(tee ${LOGFILE})
    exec 2>&1
    
    uname -a; echo
    cat /etc/redhat-release; echo
    cat /proc/cpuinfo; echo
    free -m; echo
    echo "Donezo."

    I ran it:

    $ ./script_that_logs.sh
    Linux tinkerbell.knigga.net 3.3.2-6.fc16.x86_64 #1 SMP Sat Apr 21 12:43:20 UTC 2012 x86_64 x86_64 x86_64 GNU/Linux
    
    Fedora release 16 (Verne)
    
    processor       : 0
    vendor_id       : AuthenticAMD
    cpu family      : 16
    model           : 6
    model name      : AMD Athlon(tm) II Neo K125 Processor
    stepping        : 3
    microcode       : 0x10000c8
    cpu MHz         : 1700.000
    cache size      : 1024 KB
    fpu             : yes
    fpu_exception   : yes
    cpuid level     : 5
    wp              : yes
    flags           : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 syscall nx mmxext fxsr_opt pdpe1gb rdtscp lm 3dnowext 3dnow constant_tsc up rep_good nopl nonstop_tsc extd_apicid pni monitor cx16 popcnt lahf_lm svm extapic cr8_legacy abm sse4a 3dnowprefetch osvw ibs skinit wdt nodeid_msr npt lbrv svm_lock nrip_save
    bogomips        : 3391.57
    TLB size        : 1024 4K pages
    clflush size    : 64
    cache_alignment : 64
    address sizes   : 48 bits physical, 48 bits virtual
    power management: ts ttp tm stc 100mhzsteps hwpstate
    
    
                 total       used       free     shared    buffers     cached
    Mem:          3703       3415        287          0         45       1835
    -/+ buffers/cache:       1533       2169
    Swap:         3519         19       3500
    
    Donezo.

    And then I checked the log file:

    $ ll /tmp/script_that_logs.sh.out
    -rw-rw-r--. 1 kris kris 1178 May 12 09:27 /tmp/script_that_logs.sh.out
    
    $ cat /tmp/script_that_logs.sh.out
    Linux tinkerbell.knigga.net 3.3.2-6.fc16.x86_64 #1 SMP Sat Apr 21 12:43:20 UTC 2012 x86_64 x86_64 x86_64 GNU/Linux
    
    Fedora release 16 (Verne)
    
    processor       : 0
    vendor_id       : AuthenticAMD
    cpu family      : 16
    model           : 6
    model name      : AMD Athlon(tm) II Neo K125 Processor
    stepping        : 3
    microcode       : 0x10000c8
    cpu MHz         : 1700.000
    cache size      : 1024 KB
    fpu             : yes
    fpu_exception   : yes
    cpuid level     : 5
    wp              : yes
    flags           : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 syscall nx mmxext fxsr_opt pdpe1gb rdtscp lm 3dnowext 3dnow constant_tsc up rep_good nopl nonstop_tsc extd_apicid pni monitor cx16 popcnt lahf_lm svm extapic cr8_legacy abm sse4a 3dnowprefetch osvw ibs skinit wdt nodeid_msr npt lbrv svm_lock nrip_save
    bogomips        : 3391.57
    TLB size        : 1024 4K pages
    clflush size    : 64
    cache_alignment : 64
    address sizes   : 48 bits physical, 48 bits virtual
    power management: ts ttp tm stc 100mhzsteps hwpstate
    
    
                 total       used       free     shared    buffers     cached
    Mem:          3703       3415        287          0         45       1835
    -/+ buffers/cache:       1533       2169
    Swap:         3519         19       3500
    
    Donezo.

    It seems to totally work for me.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: