简体   繁体   中英

Display stdout and stderr to both console and file, color stderr red and display stdout and stderr as they are being produced


I have a strict requirement of having *stdout* and *stderr* being simultaneously redirected to both a console and a file, with *stderr* output being colored red.
Additionally, *stdout* and *stderr* lines should be displayed in the order they are being produced (I'll explain better what I mean with this).
My issue is achieving this altogether in a bash script, as each individual steps are not really a big challenge.


a) Display *stdout* and *stderr* to both console and file
There's a lot of information how to achieve this. Something like:
 mycommand 2>&1 | tee mylog.log

But, as explained before, I need more than that.

b) Color stderr red on a console
Again, not a big issue:

 exec 3>&1 mycommand 2>&1>&3 | sed $'s,.*,\e[31m&\e[m,' EXIT_CODE=${PIPESTATUS[0]} #exit status of command exec 3>&- #housekeeping, close file handle 3

Basically, every stdout line is being injected with ansi codes to turn them red. Of course, an ansi-compatible console is required - I'm using cmder/ConEmu for Windows.

Anyway, the output is exactly what I need. In fact, this is what I meant with " stdout and stderr being displayed in the order they are being produced":

正确的输出 (Fig.1 - Right output)

c) Joining all together
This should also be straightforward with something like:

 myfunc() { exec 3>&1 set -o pipefail mycommand 2>&1>&3 | sed $'s,.*,\e[31m&\e[m,' EXIT_CODE=${PIPESTATUS[0]} #exit status of $EXEC_COMMAND exec 3>&- #housekeeping, close file handle 3 echo exit_code: $EXIT_CODE } myfunc | tee mylog.log echo "EXIT_CODE: $EXIT_CODE"

But it is not straightforward. At least not for me, namely because now mycommand's stderr is only being displayed after I exit my app (which was executed running "mycommand"):

错误的输出 (Fig.2 - Wrong output)

Also, as a side effect, I cannot manage to get the EXIT_CODE value outside the function.
I saw similar issues on this, but all seemed to be related with while loops running in a subshell, "redirection" and\or "process substitution". I still wasn't able to solve my specific issue.


So, my questions are:

  1. How to have stdout and stderr to both console and file, having red stderr lines and "stdout and stderr being displayed as they are being produced" (See Fig.1)?
  2. How to get the EXIT_CODE value outside the function body?


Thank you for your time!

namely because now mycommand's stderr is only being displayed after I exit my app

How to have stdout and stderr to both console and file, having red stderr lines and "stdout and stderr being displayed as they are being produced" (See Fig.1)?

Both stderr and stdout of your program become block buffered when the output is not a terminal. stdout is block buffered because mycommand is not outputting to the terminal and sed s output is not a terminal and it's outputting stderr, so stderr becomes also block buffered. Configure your program to configure it's stdout/stderr to be line buffered, or you could potentially set initial buffering with stdbuf -oL -eL and use sed -u or stdbuf -oL sed .

How to get the EXIT_CODE value outside the function body?

  1. Create a temporary file.
  2. Write $EXIT_CODE to a temporary file.
  3. Read that file.

or return the exit code from your function and use PIPESTATUS as you did.

myfunc() {
  {
      stdbuf -oL -eL mycommand 2>&1 >&3 | sed -u $'s,.*,\e[31m&\e[m,'
      EXIT_CODE=${PIPESTATUS[0]}
  } 3>&1  
  echo exit_code: $EXIT_CODE
  return "$EXIT_CODE"
}
myfunc | tee mylog.log
echo "EXIT_CODE: ${PIPESTATUS[0]}"

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM