I have a bash script that is used for auto updating. I want to log all output of this script and prepend a timestamp to each line. I'm not sure how to add this to what I already have.

LOGFILE="logdir/update.sh.$(date +%Y-%m-%d_%H:%M).log"
exec 1>$LOGFILE 2>&1

This redirects the output of my script to a file correctly, but there is (obviously) no date added to the line. How can I add this?

For instance, I want the logged output to look like this:

[2019-11-07 1:43:45 PM]: Ign http://security.debian.org jessie/updates InRelease

I'm not looking to prepend anything to each individual command my script has, I'm looking to prepend the timestamp and log each line any one command outputs globally throughout the script.

  • It's not a duplicate unless I want to prepend that to each line of my script, which I do not. I don't even have confidence that would output each line that a command generates, and not just output the command itself. – sc_0987 Nov 7 '19 at 19:17
  • @sc_0987 "The command" in the duplicated question is your script. Not every individual command in the script. This would mean managing the logging from outside of the script itself, possibly from a wrapping script. – Kusalananda Nov 7 '19 at 19:21
  • @Kusalananda So in order to timestamp the output of my script I have to pipe my script to ts and send stdout to a file? That's obtuse. The goal is to run ./update.sh and that's it. – sc_0987 Nov 7 '19 at 19:26
  • ... since you are using bash (which supports process substitutions) you could replace exec 1>$LOGFILE with exec 1> >(ts > $LOGFILE) I think – steeldriver Nov 7 '19 at 19:27

You can redirect the stdout of the script to a program that adds the timestamp, and from there to a logfile using process substitution with exec. Using the ts command mentioned in this answer, this should work:

logfile="logdir/update.sh.$(date +%Y-%m-%d_%H:%M).log"
exec 1>> >(ts '[%Y-%m-%d %H:%M:%S]' > "$logfile") 2>&1
echo doing some real work...

The process substitution here works a bit like a pipe, you just can't use a regular pipe with exec. It's not a standard feature, but works in the usual set of the more featureful shells: Bash, ksh and zsh.

Of course there are other options than ts in the answers to that question: Prepending a timestamp to each line of output from a command

| improve this answer | |
  • Maybe move the >> operator before "$logfile" to append to the log, though it's not clear if OP wants to overwrite or append... – Freddy Nov 7 '19 at 19:55
  • @Freddy, hmm, they did have just 1>$LOGFILE in the Q, and with a timestamped log the file probably doesn't exist anyway. The redirection to the process substitution could of course be just 1> >(...), but that doesn't matter either... – ilkkachu Nov 7 '19 at 20:09
  • Unfortunately I have to use date as ts is not available and I can't easily obtain it (firewalls, other corporate stuff). I have tried this: exec 1>> >(date '+[%Y-%m-%d %I:%M:%S %p]: ' > "$logfile") 2>&1 but all I get is a single line with the date output and nothing else. – sc_0987 Nov 7 '19 at 21:46
  • @sc_0987, yes, date would just output the date once. what you need is a program that reads the input and passes it through with the date added. Did you look at the linked question? There's another answer there with more ways to do it, e.g. with Perl or just the shell and date. (Of course you'll have to replace the pipe structure with the process substitution) – ilkkachu Nov 7 '19 at 21:51

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