You are not logged in.

#1 2022-08-08 14:36:52

lenhuppe
Member
From: New Hampshire USA
Registered: 2018-12-10
Posts: 188
Website

[SOLVED] Capturing error messages in scripts

I am trying to use the trap built-in to capture error messages and exit gracefully. I have come up with the following:

trap '{ echo "$(date +%b%d) ${0##*/} line:$LINENO" ; exit 0 ; }' err

In my testing I tried running 'pacman -S --noconfirm false-package' and got the following:

error: target not found: false-package
Aug08 test.sh line:17

This works great in interactive mode but I need to write it all out to a log file.

How can I capture the error message itself?

I have been searching the forums with no luck.

Thank you in advance.

Last edited by lenhuppe (2022-08-15 21:16:21)

Offline

#2 2022-08-08 14:53:57

Trilby
Inspector Parrot
Registered: 2011-11-29
Posts: 27,272
Website

Re: [SOLVED] Capturing error messages in scripts

I'm not sure what the question is here ... I suspect you are familiar with output / stdstream redirection in shell scripts.  Why can't you just direct the desired output to the intended file (e.g., using the '>' or '>>' operators)?  To capture stderr output (from other processes like pacman as your "trap" output is to stdout not stderr) then just redirect the stderr to the file and/or to stdout if you are already directing stdout to a file (e.g., '2>&1')

Last edited by Trilby (2022-08-08 14:55:37)


"UNIX is simple and coherent..." - Dennis Ritchie, "GNU's Not UNIX" -  Richard Stallman

Offline

#3 2022-08-08 15:06:44

lenhuppe
Member
From: New Hampshire USA
Registered: 2018-12-10
Posts: 188
Website

Re: [SOLVED] Capturing error messages in scripts

Trilby wrote:

I'm not sure what the question is here ... I suspect you are familiar with output / stdstream redirection in shell scripts.  Why can't you just direct the desired output to the intended file (e.g., using the '>' or '>>' operators)?  To capture stderr output (from other processes like pacman as your "trap" output is to stdout not stderr) then just redirect the stderr to the file and/or to stdout if you are already directing stdout to a file (e.g., '2>&1')

I'm not sure how to redirect stderr in a trap command. I can get pretty much everything else though.

With the example above my log reads

 <date> <file> <line#> 

If possible I need it to read

  <date> <file> <line#> <error> 

Offline

#4 2022-08-08 15:17:16

twelveeighty
Member
From: Alberta, Canada
Registered: 2011-09-04
Posts: 929

Re: [SOLVED] Capturing error messages in scripts

The classic use-case for 'trap' is its asynchronous nature - you set the 'trap' and then rely on its code being executed sometime later when the condition has been met and the trap 'springs'. For example, you trap 'Ctrl-C' to gracefully exit a complex script with some cleanup statements.

It sounds like you are just trying to capture whether or not a subprocess completed successfully and then log its output/error and potentially have the calling script/process take a different direction based on the success/failure. That's better handled by 'normal' synchronous processing via redirects and capture of the subprocess' exit code.

Offline

#5 2022-08-08 16:12:48

Trilby
Inspector Parrot
Registered: 2011-11-29
Posts: 27,272
Website

Re: [SOLVED] Capturing error messages in scripts

lenhuppe wrote:

I'm not sure how to redirect stderr in a trap command.

The same as anywhere else - but your trap command doesn't generate any output that is sent to stderr.  If you mean you want the error output of some other process that is not run from your trap code, then there isn't really any way to get that in your trap code any more than you could in any other circumstance.

You can have your trap code write to stderr, and then redirect the stderr from the entire script to a log file.

But your desired outcome of '<date> <file> <line#> <error>' is ill-definined, what do you want in place of <error>?  The error number / exit code of the last process?  That's easy.  But the stderr of the last process is a stream ... it's not a singular value or string.  If you want '<date> <file> <line#>\n<all stderr content of last process>' you could redirect stderr from each process to a temp file which is then catted from the trap handler - but this would be a bit ugly, e.g.:

trap '{ echo "whatever"; cat /tmp/my.temp.log; exit 0; }' err
pacman -whatever 2>| /tmp/my.temp.log

Alternatively you could redirect stderr from the entire script right from the start:

exec 2>/tmp/my.temp.log
trap '{echo "whatever"; exit 0; }' err
pacman -whatever
pacman -another-silly-command
# ...

The potential downside is that my.temp.log would get any stderr output from all commands in the script.  Although based on what you've described so far this might not be a problem as your script exits on the first error anyways (though this assumes processes called from the script will not generate stderr output without giving a non-zero exit code).

If you are okay with the stderr output coming just before the date/file/line stamp, then this would be trivial, just have your trap write to stderr, and redirect stderr from the whole script:

exec 2>/your/intended/log.file
trap '{ echo "whatever" >&2; exit 0 }' err
pacman -whatever
pacman -something-else
#...

In any case, this is all wild speculation.  Post your actual script so we can see what we're working with.

As an aside, why do you want your script to return a zero exit status when it exited prematurely due to an error??

Last edited by Trilby (2022-08-08 16:16:19)


"UNIX is simple and coherent..." - Dennis Ritchie, "GNU's Not UNIX" -  Richard Stallman

Offline

#6 2022-08-08 17:09:39

lenhuppe
Member
From: New Hampshire USA
Registered: 2018-12-10
Posts: 188
Website

Re: [SOLVED] Capturing error messages in scripts

Trilby wrote:

In any case, this is all wild speculation.  Post your actual script so we can see what we're working with.

As an aside, why do you want your script to return a zero exit status when it exited prematurely due to an error??

Here is the problematic script which I am developing:

dropbox_setup.sh

#!/usr/bin/env bash

# error handling
set -Euo pipefail

# capture error messages and exit gracefully
trap '{ echo "$(date +%b%d) ${0##*/} line:$LINENO --> $BASH_COMMAND" >> /archive/errlog.txt ; exit 0 ; }' err

# function to print messages
print() { tput setaf 2 ; echo -e "\n\t$1\n" ; tput sgr0 ; }

# AUR packages
aur_pkgs=( dropbox dropbox-cli nautilus-dropbox )

## -- MAIN -- ##

# deps
[[ ! "$(pacman -Q python-gpgme)" ]] && pacman -S --noconfirm python-gpgme

# key
print "Installing Dropbox key"
sudo -u "$SUDO_USER" bash << _end_
curl -Ls https://linux.dropbox.com/fedora/rpm-public-key.asc | gpg --import -
_end_

# install from AUR
for pkg in "${aur_pkgs[@]}" ; do
	print "Building $pkg"
	build=/tmp/"$pkg"
	sudo -u "$SUDO_USER" bash <<- _end_
	trap 'rm -rf $build' err exit
	git clone -q https://aur.archlinux.org/"$pkg" "$build"
	cd "$build" ; makepkg --noconfirm -sic
	_end_
done

# home directory changes
home=/home/"$SUDO_USER"

# disable auomatic updates
sudo -u "$SUDO_USER" bash << _end_
[[ -d "$home/.dropbox-dist" ]] && rm -rf $home/.dropbox-dist
install -dm0 $home/.dropbox-dist
_end_

# nautilus bookmarks
sudo -u "$SUDO_USER" bash << _end_
cat >> "$home"/.config/gtk-3.0/bookmarks << _eof_
file://$home/Dropbox Dropbox
file://$home/Dropbox/Build/ArchZFS ArchZFS
file://$home/Dropbox/Easterseals/Schedule Schedule
_eof_
_end_

This script is called by another script so I want it to exit upon error and allow the calling script to continue. I agree with you that there are better ways to do that than using a trap.

However the trap I created does record some valuable information even if it does not capture the actual error message, and that allowed me to zero in on the issue.

I should remove the "exit 0" from the trap and find another way to allow the calling script to continue.

I suspect that I should also avoid having two trap commands as well?

Offline

#7 2022-08-08 17:30:58

Trilby
Inspector Parrot
Registered: 2011-11-29
Posts: 27,272
Website

Re: [SOLVED] Capturing error messages in scripts

I'd really change up the logic of that script a lot.  It seems it is run as root, just so the first pacman command can run, but then everything else not only doesn't require root, but in fact is "sudoed" to another user.  But then on top of that, some of the commands run in those heredocument scripts run as a regular user themselves call sudo again (how do those even work, they require passwords [ both the -s and -i flags to makepkg make it call `sudo pacman ...` ]).

So it could be something more like the following:

#!/usr/bin/env bash

exec 2>>/archive/errorlog.txt

set -Euo pipefail

trap 'echo "$(date +%b%d) ${0##*/} line:$LINENO --> $BASH_COMMAND" > 2' err

print() { tput setaf 2 ; echo -e "\n\t$1\n" ; tput sgr0 ; }

aur_pkgs='dropbox dropbox-cli nautilus-dropbox'

sudo pacman -S --noconfirm --needed python-gpgme

print "Installing Dropbox key"
curl -Ls https://linux.dropbox.com/fedora/rpm-public-key.asc | gpg --import -

cd /tmp
for pkg in $aur_pkgs; do
	print "Building $pkg"
	git clone -q https://aur.archlinux.org/$pkg
	cd $pkg
	makepkg --noconfirm -sic
	cd ..
done

cd
rm -rf .dropbox-dist
install -dm0 .dropbox-dist

cat >> .config/gtk-3.0/bookmarks << _eof_
file://$HOME/Dropbox Dropbox
file://$HOME/Dropbox/Build/ArchZFS ArchZFS
file://$HOME/Dropbox/Easterseals/Schedule Schedule
_eof_

As for the exit code - if you wrote the other script and want it to continue regardless of this scripts exit code, then just do that - no need to give a zero exit code from this script.

But if it were me, I'd get rid of the traps entirely.  Just set the script to send it's stderr to your log file (as in my example code) then any problem from the makepkg / pacman calls will be logged there.  If you want you can add a timestamp at the start of the script:

exec 2>>/archive/errorlog.txt
echo "Starting $0 on $date" >2

Then if anything goes wrong in the script, your errorlog.txt will include the script name, the date that it ran, and then any error output from the failed command.  You may also want your "print" command to log to stderr too so you get even more detail of where something went wrong ... but again, it's a very simple script, so the actual stderr output from the failing process will make it quite clear where the problem was.

Last edited by Trilby (2022-08-08 17:35:27)


"UNIX is simple and coherent..." - Dennis Ritchie, "GNU's Not UNIX" -  Richard Stallman

Offline

#8 2022-08-08 17:43:43

lenhuppe
Member
From: New Hampshire USA
Registered: 2018-12-10
Posts: 188
Website

Re: [SOLVED] Capturing error messages in scripts

Trilby wrote:

I'd really change up the logic of that script a lot.  It seems it is run as root, just so the first pacman command can run, but then everything else not only doesn't require root, but in fact is "sudoed" to another user.  But then on top of that, some of the commands run in those heredocument scripts run as a regular user themselves call sudo again (how do those even work, they require passwords [ both the -s and -i flags to makepkg make it call `sudo pacman ...` ]).

So it could be something more like the following:

#!/usr/bin/env bash

exec 2>>/archive/errorlog.txt

set -Euo pipefail

trap 'echo "$(date +%b%d) ${0##*/} line:$LINENO --> $BASH_COMMAND" > 2' err

print() { tput setaf 2 ; echo -e "\n\t$1\n" ; tput sgr0 ; }

aur_pkgs='dropbox dropbox-cli nautilus-dropbox'

sudo pacman -S --noconfirm --needed python-gpgme

print "Installing Dropbox key"
curl -Ls https://linux.dropbox.com/fedora/rpm-public-key.asc | gpg --import -

cd /tmp
for pkg in $aur_pkgs; do
	print "Building $pkg"
	git clone -q https://aur.archlinux.org/$pkg
	cd $pkg
	makepkg --noconfirm -sic
	cd ..
done

cd
rm -rf .dropbox-dist
install -dm0 .dropbox-dist

cat >> .config/gtk-3.0/bookmarks << _eof_
file://$HOME/Dropbox Dropbox
file://$HOME/Dropbox/Build/ArchZFS ArchZFS
file://$HOME/Dropbox/Easterseals/Schedule Schedule
_eof_

As for the exit code - if you wrote the other script and want it to continue regardless of this scripts exit code, then just do that - no need to give a zero exit code from this script.

But if it were me, I'd get rid of the traps entirely.  Just set the script to send it's stderr to your log file (as in my example code) then any problem from the makepkg / pacman calls will be logged there.  If you want you can add a timestamp at the start of the script:

exec 2>>/archive/errorlog.txt
echo "Starting $0 on $date" >2

Then if anything goes wrong in the script, your errorlog.txt will include the script name, the date that it ran, and then any error output from the failed command.

I am not a programmer but I can see how much better your method is. The "sudo -u" thing was found online and while it works I never really liked it.

It seems that I have work to do rewriting some things. Much appreciated.

Offline

#9 2022-08-15 21:15:50

lenhuppe
Member
From: New Hampshire USA
Registered: 2018-12-10
Posts: 188
Website

Re: [SOLVED] Capturing error messages in scripts

Solution:

I initiated this thread because I wanted to capture error messages and exit gracefully if needed. I found three ways to do this:

Option #1

exec 2>>/archive/errlog.txt
echo "Starting $0 on $(date +%b%d)" >&2

This method gathers a lot of information and logs it. In some cases it will log warning messages. 

Option #2

trap '{ echo "$(date +%b%d) ${0##*/} line:$LINENO -> $BASH_COMMAND" ; exit ; }' err

This is in effect an enhanced 'set -e' command. I find it very handy  when developing new scripts.

Option #3

trap '{ echo "$(date +%b%d) ${0##*/} line:$LINENO -> $BASH_COMMAND" >> /archive/errlog.txt ; exit 0 ; }' err

This works well for scripts that are called by other scripts. It exits gracefully after logging errors and allows the calling script to continue.

many thanks to the community

Last edited by lenhuppe (2022-08-15 21:17:24)

Offline

Board footer

Powered by FluxBB