Output stdout and stderr to file and screen and stderr to file in a limited environment
The problem could be probably fixed using mkfifo
, but it doesn't exist on my QNAP. So, here is the description of problem and what I tried so far.
I have a function called activateLogs
that restarts the script if writing logs to disk or both (screen and disk). Both option is the new functionality I would like to achieve.
exec 3<> "$logPath/$logFileName.log"
"$0" "${mainArgs[@]}" 2>&1 1>&3 | tee -a "$logPath/$logFileName.err" 1>&3 &
This piece of code is the version that writes to disk. mainArgs
contains all the arguments passed to the script and is defined out of this function. This solution came from https://stackoverflow.com/a/45426547/214898. It combines stderr & stdout in a file and still output stderr in another.
So, now, I would like to be able to keep this and add printing stderr & stdout to screen.
The accepted solution from the question linked above cannot be applied because the script is running using sh
and mkfifo
is not present.
Attempt #1
exec 3>&1
"$0" "${mainArgs[@]}" 2>&1 1>&3 | tee -a "$logPath/$logFileName.err" 1>&3 &
--> to replace the code above and in a if
branching (that already existed) I added the tee
command.
local isFileDescriptor3Exist=$(command 2>/dev/null >&3 && echo "Y")
if [ "$isFileDescriptor3Exist" = "Y" ]; then
tee -a "logs/123.log" &
echo "Logs are configured"
else
### CODE ABOVE
fi
I have the screen, the error file, but the log file is empty.
Attempt #2
Now, no tee in the if
branching above, but included in the relaunching command.
exec 3>&1
"$0" "${mainArgs[@]}" 2>&1 1>&3 | tee -a "$logPath/$logFileName.err" 1>&3 3>&1 | tee -a "logs/123.log" &
Same result. I may understand in this one that the first tee
not initially writing to the file descriptor #3, thus, the 3>&1
does nothing.
Attempt #3 (No more relaunching the script)
out="${TMPDIR:-/tmp}/out.$$"
err="${TMPDIR:-/tmp}/err.$$"
busybox mkfifo "$out" "$err"
trap 'rm "$out" "$err"' EXIT
tee -a "$logPath/$logFileName.log" &
tee -a "$logPath/$logFileName.err" < "$err" >&2 &
command >"$out" 2>"$err"
I am getting mkfifo: applet not found
from busybox
Attempt #4 (No more relaunching the script)
out="${TMPDIR:-/tmp}/out.$$"
err="${TMPDIR:-/tmp}/err.$$"
python -c "import os; os.mkfifo(\"$out\")"
python -c "import os; os.mkfifo(\"$err\")"
trap 'rm "$out" "$err"' EXIT
tee -a "$logPath/$logFileName.log" &
tee -a "$logPath/$logFileName.err" < "$err" >&2 &
command >"$out" 2>"$err"
I have no logs (neither "real" logs" nor errors). The temporary files are deleted though. Moreover, the script never ends which is caused by trap
.
Attempt #5
exec 3>&1
{ "$0" "${mainArgs[@]}" | tee -a "$logPath/$logFileName.log"; } 2>&1 1>&3 | tee -a "$logPath/$logFileName.err" &
exit
That solution seemed promising, but now relaunching the script doesn't work because my code detects if currently executing and stop it. This is normal because it is executed in subprocess even though I use &
at the end of the full line, but... (testing while writing). Replacing the terminator ;
by &
fixed it.
Attempt #6
I didn't realized it right away, but stdout & stderr were displayed to screen, stderr was written to a file, but only stdout was written to a file instead of both.
exec 3>&1
{ "$0" "${mainArgs[@]}" | tee -a "$logPath/$logFileName.log" & } 2>&1 1>&3 | tee -a "$logPath/$logFileName.err" &
exit
Final version working
Now everything is written/displayed where it is supposed to be. See full code in the accepted answer.
{ "$0" "${mainArgs[@]}" 2>&1 1>&3 | tee -a "$logPath/$logFileName.err" 1>&3 & } 3>&1 | tee -a "$logPath/$logFileName.log" &
exit
Incredible how my first attempt was so close to the solution.
Maybe I'm not reading the question correctly, but it seems that you could do something like:
#!/bin/sh
exec 3>&1
cmd (){
echo stdout;
echo stderr >&2;
}
stderr_outfile=outfile
if test -n "$log_stdout"; then
stdout_outfile=$log_stdout
else
stdout_outfile=/dev/null
fi
{ cmd | tee "$stdout_outfile"; } 2>&1 1>&3 | tee "$stderr_outfile"