1

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

  • Possible duplicate of Redirect all standard out for rest of script – Michael Homer Jan 5 '18 at 20:50
  • @MichaelHomer Not really, this question calls for different answers because there's the conditional aspect. – Gilles Jan 5 '18 at 20:57
  • 1
    @Gilles I'm not sure that "wrap an if around it" is a material difference (especially when the question already asks what to put into the if...). – Michael Homer Jan 5 '18 at 21:00
  • @MichaelHomer The point is that there are methods whch are not “wrap an if around it”. – Gilles Jan 5 '18 at 21:01
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
    
  • 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 8 '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 8 '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 8 '18 at 17:26

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.