Append stderr

Hi everybody.

I was used to redirect stderr to a file in this way, calling a generic script:

./myScript &> output.log

But now I need something more sophisticated...Inside a bash script I launch an executable in this way:

${command}  >> "${globalLogFile}"

So I redirect the stdout into globalLogFile.
How to contemporary redirect the stderr to another log file, APPENDING it?

Thanks in advance!

Something like:

${command}  >> "${globalLogFile}" 2>> $globalErrFile

Ok, thanks, pretty simple...

And is it possible to give the stderr as input to a fuction?

For example, I have this function:

log() {
    echo "$(date) - $1" >> "output.log"
}

Is it possible to do something like (pseudocode):

${command}  >> "${globalLogFile}" 2>> log "&2"

or something?

---------- Post updated at 08:36 AM ---------- Previous update was at 05:01 AM ----------

No ideas?

time zones are funny things, aren't they...?

-> ls -ltr nowhere.txt 2>&1 |tee -a /tmp/error_test.log
nowhere.txt: No such file or directory

-> cat -n /tmp/error_test.log                          
     1  nowhere.txt: No such file or directory
     2  nowhere.txt: No such file or directory

Excuse me, but I do not understand your answer...No ideas for the stderr/log stuff?

This is a forum more than a chat, so patience is sometimes needed before you get a response.

Meanwhile, you can capture your STDERR into your STDOUT using something like my sample, or using your own sample:

$(command) 2>&1 |tee -a [log.file] 

This will allow your error to echo to screen, while it also appends to the named file.

Hi.

Here's an example, of what I think you are asking.

$ cat LogTest
LogError() {
  while read LINE; do
    echo "ERROR:  $LINE"
  done
}

PrintSomething() {
  echo "OK"
  echo "ERROR1" >&2
  echo "ERROR2" >&2
}

PrintSomething 2>&1 > some_log_file.txt | LogError


$ ./LogTest
ERROR:  ERROR1
ERROR:  ERROR2


$ cat some_log_file.txt
OK

Errors go through function LogError - and from there you can do with it what you wish.

Thank you scottn and sorry for having bumped the thread up.
I've tryed to adapt your script to my needs, without success.

I have:

log() {
    while read LINE; do
        echo "$(date) - ${LINE}" >> "${LOGFILE}"
    done
}

${PROGRAM}  >> "${LOG_FILE_1}" 2>&1 >> ${LOGFILE_2} | log

But the scripts stops in reading lines and doesn't go ahead...

Which changes should I make?

Hi.

This bit is wrong:

${PROGRAM} >> "${LOG_FILE_1}" 2>&1 >> ${LOGFILE_2} | log

Should be

${PROGRAM} 2>&1 >> ${LOGFILE_2} | log

If you want to write to two log files, you should combine this with tee:

${PROGRAM}  2>&1 | tee -a "${LOG_FILE_1}" >> ${LOGFILE_2} | log

I just ran this on ksh and bash on AIX, Linux and Solaris, and sh on Solaris, and it works the same on all of them - but only when feeding log through a pipe - not calling it directly. What OS/Shell are you using?

I'm working on a Red Hat Enterprise 4 AS with GNU bash, version 3.00.15(1)-release (i386-redhat-linux-gnu).

I tryed your script but I obtain either stdout and stderr in both log files...

Maybe it would be best if you showed your script - exactly what you are doing - including where you set variables, like LOGFILE, LOG_FILE_1, LOGFILE_2, PROGRAM, etc...

Are you sure following helped you to capture stderr into output.log log file?
It appears you are directing stdout to output.log in a non-appending mode.

./myScript &> output.log

Did you try this ?

./myScript & 1>>output.log 2>>errors.log

Your code -->

${command}  >> "${globalLogFile}"

How about this ?

${command}  >> "${globalLogFile}" 2>>"${globalerrorLogFile}

The closest solution I have tryed is the following:

log() {
    while read LINE; do
        echo "$(date) - ${LINE}" >> "${LOGFILE_2}"
    done
}

${command}  >> "${LOGFILE_1}" 2>>"${LOGFILE_2} | log

I obtain the stdout inside $LOGFILE_1 and the stderr inside $LOGFILE_2 BUT without the date before it, as I wanted to.
Simply the log function is not called at all.

Variables $LOGFILE_1 and $LOGFILE_2 are something like

LOGFILE_1=/home/canduc/file1.log
LOGFILE_2=/home/canduc/file2.log

, so nothing special.

$command is an executable written in C a bit hard to explain what it does...but it prints strings either on stdout and stderr, I think it is enough to describe my problem.

I tryed also with

log() {
    while read LINE; do
        echo "$(date) - ${LINE}" >> "${LOGFILE_2}"
    done
}

${command} 2>>"${LOGFILE_2} | log 

but I obtain the stdout (with date) in the log file and

log() {
    while read LINE; do
        echo "$(date) - ${LINE}" 2>> "${LOGFILE_2}"
    done
}

${command} 2>>"${LOGFILE_2} | log 

(notice the "2>>" inside the function), but I obtain the stderr inside the log, WITHOUT the date.

I think that the problem can be summarized into this question: how to pass the stderr to a function?

Thank you for your patience...

Hi.

There is no point in directing output of standard error in your log function using 2>> because there never is any.

SomeFunction 2>&1 >> ${LOGFILE_2} | log
  1. Standard error is directed to where standard output is going at this point in time (i.e. the screen, standard output)
  2. Standard output is then sent to LOGFILE_2
  3. Standard error (still pointing to where standard output was pointing before it was changed) (i.e. the screen) is piped into the log function
  4. The log function should write standard output - not standard error - to LOGFILE
log() {
    while read LINE; do
        echo "$(date) - ${LINE}" >> "${LOGFILE_2}"
    done
}

${command}  >> "${LOGFILE_1}" 2>>"${LOGFILE_2} | log

All you are doing here is writing standard output to LOGFILE_1 and standard error to LOGFILE_2. There is nothing left for the log function to do - you left nothing to be piped into it.

log() {
    while read LINE; do
        echo "$(date) - ${LINE}" >> "${LOGFILE_2}"
    done
}
${command} 2>>"${LOGFILE_2} | log

Here, you have sent standard error to LOGFILE_2 (you're missing a quote on that line), standard output goes to the log function, which you also write to LOGFILE_2

log() {
    while read LINE; do
        echo "$(date) - ${LINE}" 2>> "${LOGFILE_2}"
    done
}

${command} 2>>"${LOGFILE_2} | log

And here, you have written standard error to LOGFILE_2, standard output goes to the log function, but you do nothing with it, as you're directing standard error to LOGFILE_2 (although there is nothing in standard error to write to the file)

$ cat Test1
LOGFILE_1=L1.txt
LOGFILE_2=L2.txt
LOGFILE=ERR.txt

log() {
    while read LINE; do
        echo "$(date) - ${LINE}" 2>> "${LOGFILE}"
    done
}

SomeFunction() {
  echo "This line goes to log file"
  echo "This line goes to error file" >&2
}

SomeFunction 2>&1 >> ${LOGFILE_2} | log


echo "Log"
cat $LOGFILE_2
echo
echo
echo Error
cat $LOGFILE
echo
echo
echo


$ ./Test1
Log
This line goes to log file


Error
$ cat Test2
LOGFILE_1=L1.txt
LOGFILE_2=L2.txt
LOGFILE=ERR.txt

log() {
    while read LINE; do
        echo "$(date) - ${LINE}" >> "${LOGFILE}"
    done
}

SomeFunction() {
  echo "This line goes to log file"
  echo "This line goes to error file" >&2
}

SomeFunction 2>&1 >> ${LOGFILE_2} | log


echo "Log"
cat $LOGFILE_2
echo
echo
echo Error
cat $LOGFILE
echo
echo
echo

$ ./Test2
Log
This line goes to log file


Error
Thu May 20 12:42:20 DFT 2010 - This line goes to error file
1 Like

Thank you so much scottn!
Now my script works as I wanted to and I have definetely better understood bash redirection.

I've reimplemented your last example in a way more similar to my case: instead of producing strings on stdout and stderr with bash, I produce them with a C program, then executed by a script.

Here's the C prog:

#include <stdio.h>
#include <unistd.h>

int main()
{
    int i;

    for(i=0; i<3; i++)
    {
        fprintf(stdout, "This is message n. %d on the standard output\n", i);
        fprintf(stderr, "This is message n. %d on the standard error\n", i);
        sleep(1);
    }

    return 0;
}

After the compilation, I have executed it with the following script:

#!/bin/bash

COMMAND="stdoutAndstderr"
OUTLOG="stdout.log"
ERRLOG="stderr.log"

log()
{
    while read LINE; do
        echo "$(date +"%D %T") - ${LINE}" >> ${ERRLOG}
    done
}

$(pwd)/${COMMAND} 2>&1 >> ${OUTLOG} | log

echo "${OUTLOG}:"
cat ${OUTLOG}
echo ""
echo "${ERRLOG}:"
cat ${ERRLOG}

exit 0

And the output is

 $ ./stdoutAndstderr.sh
stdout.log:
This is message n. 0 on the standard output
This is message n. 1 on the standard output
This is message n. 2 on the standard output

stderr.log:
05/20/10 15:18:52 - This is message n. 0 on the standard error
05/20/10 15:18:53 - This is message n. 1 on the standard error
05/20/10 15:18:54 - This is message n. 2 on the standard error

Hope to be helpful for someone!