3

I'm trying to write myself a logging function for my bash shell. My goal is to have the function that can preface a command and will pass the stdout and stderr of the command to dated logfiles. Currently I have:

logme()
{
    FULL_COMMAND=$@

    for EXE in ${FULL_COMMAND}
    do
        case $EXE in
            nohup) continue;;
            time) continue;;
        esac
        break
    done

    mkdir -p logs
    LOGNAME=$(basename ${EXE})_$(date +%Y-%m-%d_%H:%M:%S)

    echo $FULL_COMMAND > logs/${LOGNAME}.e.log
    echo '============' >> logs/${LOGNAME}.e.log
    $FULL_COMMAND > logs/${LOGNAME}.o.log 2>> logs/${LOGNAME}.e.log
}

This seems to work for simple cases. Running:

$ touch file1.txt
$ touch file2.txt
$ touch file3.txt

$ logme ls -lh

produces a logs directory with 2 dated files.

$ more logs/ls_2017-05-08_16\:05\:36.e.log 
ls -lh
============

$ more logs/ls_2017-05-08_16\:05\:36.o.log 
total 6.5K
-rw-r--r-- 1 aholman compbio  0 May  8 16:05 file1.txt
-rw-r--r-- 1 aholman compbio  0 May  8 16:05 file2.txt
-rw-r--r-- 1 aholman compbio  0 May  8 16:05 file3.txt
drwxr-xr-x 2 aholman compbio 92 May  8 16:05 logs

However, adding pipes in the command doesn't behave as I'd hoped. The logging seems to group with the first command, and the output of logme is passed through the pipe. Of course, there is no output from logme because the whole purpose is to direct it to files.

$ logme ls -lh | grep -v file2
$ more logs/ls_2017-05-08_16\:12\:15.e.log 
ls -lh
============

$ more logs/ls_2017-05-08_16\:12\:15.o.log 
total 6.5K
-rw-r--r-- 1 aholman compbio  0 May  8 16:05 file1.txt
-rw-r--r-- 1 aholman compbio  0 May  8 16:05 file2.txt
-rw-r--r-- 1 aholman compbio  0 May  8 16:05 file3.txt
drwxr-xr-x 2 aholman compbio 92 May  8 16:12 logs

Is there a good way to group the whole command such that the entire thing gets processed by logme?

  • You could put it in { …; }, not sure whether there will be side effects though. – phk May 8 '17 at 21:17
  • Thanks for your suggestion. Not sure if I understand the syntax you're suggesting. { logme ls -lh | grep -v file2 ; } has the same effect as illustrated above. logme { ls -lh ; } fails with a bash syntax error. – A Holman May 9 '17 at 14:37
  • FYI, I'd also be open to a solution that modifies the logme script. I'm not married to that code. – A Holman May 9 '17 at 14:40
1

The shell is processing the command line before your script sees it. It only passes what remains after that processing (that command before the pipe). To bypass such processing you need to quote the whole command:

logme 'ls -lh | grep -v file2'

You have a new problem though. The command is in a string and you can't execute it directly. One way to do it is with eval

eval "$FULL_COMMAND" > logs/${LOGNAME}.o.log 2>> logs/${LOGNAME}.e.log

But eval should be used with caution as it is easily exploitable: http://mywiki.wooledge.org/BashFAQ/048

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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