Comment by hielke
12 hours ago
With exec you can open file descriptors of your current process.
if [[ ! -e /proc/$$/fd/3 ]]; then
# check if fd 3 already open and if not open, open it to /dev/null
exec 3>/dev/null
fi
>&3 echo "will print on fd 3"
This will fix the error you are describing while keeping the functionality intact.
Now with that exec trick the fun only gets started. Because you can redirect to subshells and subshells inherit their redirection of the parent:
set -x # when debugging, print all commands ran prefixed with CMD:
PID=$$
BASH_XTRACEFD=7
LOG_FILE=/some/place/to/your/log/or/just/stdout
exec 3> >(gawk '!/^RUN \+ echo/{ print strftime("[%Y-%m-%d %H:%M:%S] <PID:'$PID'> "), $0; fflush() }' >> $LOG_FILE)
exec > >(sed -u 's/^/INFO: /' >&3)
exec 2> >(sed -u 's/^/ERROR: /' >&3)
exec 7> >(sed -u 's/^/CMD: /' >&3)
exec 8>&1 #normal stdout with >&8
exec 9>&2 #normal stderr with >&9
And now your bash script will have a nice log with stdout and stderr prefixed with INFO and ERROR and has timestamps with the PID.
Now the disclaimer is that you will not have gaurantees that the order of stdout and stderr will be correct unfortunately, even though we run it unbuffered (-u and fflush).
Nice! Not really sure the point since AI can bang out a much more maintainable (and sync'd) wrapper in go in about 0.3 seconds
(if runners have sh then they might as well have a real compiler scratch > debian > alpine , "don't debug in prod")