Search code examples
shellunixhttp-redirectstdoutstderr

ksh on BSD style UNIX. How to redirect stderr to tee and a file


A similar question as been asked here

But it wont work for me

"shell.ksh"  > >(tee here.log ) 2> >(tee err.log)
ksh: 0403-057 Syntax error: `>' is not expected.

this command will not redirect stderr just stdop

"shell.ksh" | tee file.log 2>&1 

I am on AIX running some UNIX similar to BSD that does not have GNU extensions and this is ksh

ps $$
      PID    TTY STAT  TIME COMMAND
 40632390  pts/6 A     0:00 -ksh

Any short and sweet solns folks ?


Solution

  • Here is a wrapper script that should do what you want. I can't confirm that it works on AIX ksh, but it works using sh (AT&T Research) 93u+ 2012-08-01 (Debian's ksh package calls it the "Real, AT&T version of the Korn shell." This is intentionally neither pdksh nor its mksh fork).

    This is portable and should work in all POSIX shells.

    pipe1+2.ksh

    #!/bin/ksh
    
    touch err.log here.log      # create the logs now so tail -f doesn't complain
    tail -f here.log &          # follow the output we'll pipe to later (run in bg)
    HERE=$!                     # save the process ID of this backgrounded process
    tail -f err.log >&2 &       # follow output (as above), pipe output to stderr
    ERR=$!                      # save the process ID
    
    "$@" > here.log 2> err.log  # run given command and pipe stdout and stderr
    RET=$?                      # save return value
    
    sleep 1                     # tail polls at 1/s, so wait one second
    
    kill $HERE $ERR             # stop following those logs
    
    exit $RET                   # restore the exit code from given command
    

    So you invoke it as pipe1+2.ksh shell.ksh (assuming both commands are in your path).

    Here's a test version of shell.ksh:

    #!/bin/ksh
    
    echo this output is stdout 1
    echo this output is stderr >&2
    echo this output is stdout 2
    

    and a trial run:

    # ksh pipe1+2.ksh ksh shell.ksh
    this output is stdout 1
    this output is stdout 2
    this output is stderr
    # cat here.log
    this output is stdout 1
    this output is stdout 2
    # cat err.log
    this output is stderr
    

    A note: because tail -f isn't instantaneous, stdout vs stderr will be slightly out of order. It won't even be consistent between runs, at least with code that runs as fast as echo.

    I doubt your version of tail supports it, but GNU tail has a -s SECONDS flag that lets you specify a different polling interval as a decimal value, so you can try e.g. tail -f -s 0.01 … to improve the ordering of what is displayed. If your version of sleep also supports decimal values, change it to match that number.

     

    (I had originally crafted a complicated file descriptor piping response based on one of the "more elaborate combinations" in the amazing Csh Programming Considered Harmful, but that didn't seem to work. See the comments and/or the initial version of this answer.)