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"