1

I just have written a simple logger which append a message with time to a file. Now I also want to add error out to that log file for better understanding what went wrong. Here is my current code:

#!/bin/bash

logprint() {
echo "$(date +%T): $*" >> ./logfile
}

logdate() {
DATE=`date "+%d.%m.%Y"`
echo "-------------------- ${DATE} --------------------" >> ./logfile
}

The log print function takes arguments and simply write the date plus the message to the log file. The log date function simply writes the date at the beginning.

Now I would like to give the error output to the log print function. Whats the best way to do that?

Inian
  • 80,270
  • 14
  • 142
  • 161
John Mamor
  • 83
  • 8

2 Answers2

3

You can use process substitution technique of form > >(cmd) for this. This allows you to re-direct the output from the standard error stream to the function. You can do something like

2> >(logprint)

But you can't read from the output of this process-substitution as if you were reading from the positional arguments, you need to read as if you were reading over standard input. You can tweak your function to something like below. Added a script for demonstration purposes

#!/usr/bin/env bash

logprint() {

    args=""
    if (( "$#" > 0 )); then
        args="$*"
    else
        while IFS= read -r line; do
            args+="$line"
        done
    fi

    echo "$(date +%T): $args" >> ./logfile
}

logprint "foobar"
mv foobar nosuchfile 2> >(logprint)
Inian
  • 80,270
  • 14
  • 142
  • 161
  • The error messages will be in one line in the log file or each error line in one line in the log file? – John Mamor May 07 '19 at 08:28
  • @JohnMamor: You can run it once yourself and see the contents of `logfile`. Each call to `logprint` will be separate line – Inian May 07 '19 at 08:48
  • I tried your solution and it works but the timing is wrong. If I redirect the error to the function, it writes it after a normal logprint with a message though the redirect happens in my code before the normal log print. – John Mamor May 07 '19 at 12:11
  • 1
    STDERR is unbuffered. If you redirect both SDTOUT and STDERR to a common log you will see this pretty often. One solution is to channel both to a common stream. Maybe `logprint "$( cmd 2>&1 )"`, since it accepts arguments. – Paul Hodges May 07 '19 at 13:49
  • @PaulHodges so I can enter 2> as arguments? Then I don't need the read part? – John Mamor May 07 '19 at 14:17
  • c.f. the [bash manual](https://www.gnu.org/software/bash/manual/html_node/Redirections.html) and [this description](https://stackoverflow.com/questions/39110485/difference-between-and-in-bash) of subshell evaluations. – Paul Hodges May 07 '19 at 19:36
0

If you need the timestamp to be in the same line, the simplest way I can think of is partially filling the line with timestamp (without ending with a newline) and then redirect the error output.

echo -n "$(date) " >> error.txt
ls no_such_file_here 2>> error.txt  # an error message is generated here
echo "" >> error.txt   # add a newline (useful in case an error message is not produced above)

Note that the last echo is to add a new line character to the current line as it guarantees anything appended later will not be added to the same line. (However, that can result in an empty line if an error is generated in the line above.)

Update: I was assuming that you are referring to the STDERR stream. However if that is not the case, the same idea can be used.

Anubis
  • 6,995
  • 14
  • 56
  • 87