2

I want to create a script that, when called interactively, writes to stdout, but if called from another script and variable EVENT_LOGGER is defined by the calling script, writes to file EVENT_LOGGER.

There's probably an easy way to do this but it's just not coming to me. I can certainly add this logic to every place where output is generated:

if [[ -f $EVENT_LOGGER ]]
then
   echo "Some message" >> $EVENT_LOGGER
else
   echo "Some message"
fi

but that'll add a lot of bulk to the script.

I was hoping I could do something like this instead:

if [[ ! -f $EVENT_LOGGER ]]
then
   EVENT_LOGGER = "&1"
fi

Then, all output commands would be:

echo "Some message" >> $EVENT_LOGGER

and they'd go to either the file or stdout, whichever EVENT_LOGGER point to.

That didn't work. Is there another way to have $EVENT_LOGGER resolve to stdout?

I'm using ksh93 on AIX 7.1

Scavenger
  • 145

2 Answers2

4

On many Unix variants, you can access standard output via the file name /dev/stdout, thus:

if [[ -z $EVENT_LOGGER ]]; then
  EVENT_LOGGER=/dev/stdout
fi
…
echo >>"$EVENT_LOGGER" 'This is a log message'

However I'm not sure if it's available on AIX. See Portability of "> /dev/stdout" and Unix systems without /dev/stdin, /dev/stdout, and /dev/stderr?

Alternatively, keep all logging to stdout and optionally redirect stdout to a file. If you call the exec builtin with no command name but with redirections, this sets the redirections for the remainder of the script.

if [[ -n $EVENT_LOGGER ]]; then
  exec >>"$EVENT_LOGGER"
fi
…
echo 'This is a log message'

Note that output from programs called by the script will go to the same log file. If you don't want that, then do the logging on another file descriptor.

if [[ -n $EVENT_LOGGER ]]; then
  exec 3>>"$EVENT_LOGGER"
else
  exec 3>&1
fi
…
echo >&3 'This is a log message'

Yet another approach is to define a function in different ways depending on what is desired.

if [[ -n $EVENT_LOGGER ]]; then
  log () {
    echo "$*" >>"$EVENT_LOGGER"
  }
else
  log () {
    echo "$*"
  }
fi
…
log 'This is a log message'

Whatever you do, I recommend using a function. This way, if you ever want to change how logging works (e.g. both to the terminal and to a file, add colors, make it conditional, …), it'll be a lot easier.

Some general notes:

  • The test -f tests whether a file exists. This is not a good test here. Instead, test if the variable is non-empty, with -n (-z tests whether it's empty).
  • You can't put spaces around the = sign in a variable assignment.
1

You want this:

$ cat interactive.sh
#!/bin/bash
if [[ -n "$EVENT_LOGGER" ]]; then
    exec 1>>"$EVENT_LOGGER"
fi
date
echo "hello world"

The magic sauce: exec 1>>"$EVENT_LOGGER" redirects stdout for the duration of the script.

Demo:

  1. variable not defined, output to stdout

    $ bash interactive.sh
    Sat Jan  6 19:06:02 EST 2018
    hello world
    
  2. variable defined, output to file

    $ env EVENT_LOGGER="./event.logger" bash interactive.sh
    $ cat event.logger
    Sat Jan  6 19:06:13 EST 2018
    hello world
    
glenn jackman
  • 85,964
  • Thanks Gilles and Glenn. I checked and found that AIX 7.1 does support redirecting to /dev/stdout (AIX 6.1 does NOT). I think I'll go that route to redirecting the output only because, in my mind, it's more obvious what's being done. However, the 'exec 1>>$file' is an interesting use of exec. – Scavenger Jan 08 '18 at 16:12
  • I have never used the exec command before and reading about it because of thread has been educational. :-) If 'exec' is being used explicitly to run another file in the same process context as the caller, why not just do a ". some-file" versus "exec some-file"? Are they the same? I'm sure I just don't understand fully what either does. Thanks again. – Scavenger Jan 08 '18 at 16:12
  • In this context, we're providing exec with a redirection. That takes effect for the currently running script. The difference between . some-file and exec some-file: . (or source) executes the named file and then continues processing the current script; exec replaces the current process with some-file, so when some-file exits, we cannot continue with the current script. – glenn jackman Jan 08 '18 at 17:26