0

I have a script that attempts to log its actions. There's a single tee in there that does all of this. Currently, when the $LOG_FILE cannot be written to the script dies. Instead, I would like it to just not write to a log file.

In the script, the part that's logged looks something like this:

for foo in "${array_of_foos[@]"; do
  {get bar from foo} | sort -u
done | xargs -n 100 -P 20 bar_processor.sh |& tee "$LOG_FILE"

When the user does not want to write a log file, can I just set LOG_FILE=/dev/null or is there some better way to handle that? Note that this script is not and will never be run as root.

OstermanA
  • 113
  • 2
    Why would the script just die if $LOG_FILE can't be written to? I don't think tee should just up and die even itself if it can't write to a file, let alone kill the whole script. Unless you're using set -e, but you didn't mention that. – ilkkachu Sep 10 '21 at 09:29
  • It dies because I kill it, due to not being sure how to handle a non-logging use case. Thus this question. :) – OstermanA Sep 10 '21 at 21:18

1 Answers1

2

In order to handle either a log file that's writable or a log file that doesn't yet exist but that is in a writable directory, the most reliable approach is to try it:

LOG_FILE=loggit.log
( printf "" >>"$LOG_FILE" ) 2>/dev/null || LOG_FILE=/dev/null

...

echo "Stuff to be logged" >>"$LOG_FILE" some | pipeline >>"$LOG_FILE"

In your particular situation you want output to be duplicated to stdout and to the log file, so you can now do this:

some | pipeline | tee -a "$LOG_FILE"

If you want to do different things depending on whether or not you have a meaningful log file, we can handle this with a more complex scenario: you can set LOG_FILE= (ie to nothing) in the snippet above, and then test it in your later code:

[[ -n "$LOG_FILE" ]] && echo "Stuff to be logged" >>"$LOG_FILE"

and you can execute code unconditionally, while still logging if possible, with a construct like this:

some | pipeline >>"${LOG_FILE:-/dev/null}"
some | pipeline | tee -a "${LOG_FILE:-/dev/null}"

Also see my answer to another question Ways to append text to a file about a method to avoid repeating "${LOG_FILE:-/dev/null}" each time.

Chris Davies
  • 116,213
  • 16
  • 160
  • 287
  • wouldn't just LOG_FILE=${LOG_FILE:-/dev/null} do for avoiding repeating the default value? :) – ilkkachu Sep 10 '21 at 09:30
  • @ilkkachu there are two situations here. The first one is the easiest - all writes to the log file go to "$LOG_FILE" regardless. The second situation uses the empty/not-empty state of "$LOG_FILE" to identify whether or not certain sections of code should be run at all. In this scenario you can't assign /dev/null to the variable because then it's no longer an empty string so you can't test that – Chris Davies Sep 10 '21 at 10:31
  • You are correct that if I just want something as simple as >>${LOG_FILE:-/dev/null} that's trivial, but I want it to always print to screen and conditionally write to file. I'm not sure how to handle that without doing something insane like buffering the log in an array. That seems like a terrible solution, but I'm not seeing any other safe ways to do that. – OstermanA Sep 10 '21 at 21:20
  • @OstermanA I've added the necessary command structure for you to have output to stdout and optionally to a log file – Chris Davies Sep 11 '21 at 07:10
  • @roaima OK, so tee -a /dev/null is "safe" to use? That makes sense. Are there any circumstances where it's not safe to do this? – OstermanA Sep 12 '21 at 08:04
  • /dev/null is a sink. You can throw anything and everything you like at it and the data stream is simply discarded. There is never any circumstance when this does not apply. In direct answer to your question, no, there are no circumstances where it's not safe to do this. – Chris Davies Sep 12 '21 at 08:10