You could use GNU screen
's vertical split feature:
#! /bin/bash -
tmpdir=$(mktemp -d) || exit
trap 'rm -rf "$tmpdir"' EXIT INT TERM HUP
FIFO=$tmpdir/FIFO
mkfifo "$FIFO" || exit
conf=$tmpdir/conf
cat > "$conf" << 'EOF' || exit
split -v
focus
screen -t stderr sh -c 'tty > "$FIFO"; read done < "$FIFO"'
focus
screen -t stdout sh -c 'read tty < "$FIFO"; eval "$CMD" 2> "$tty"; echo "[Command exited with status $?, press enter to exit]"; read prompt; echo done > "$FIFO"'
EOF
CMD="$*"
export FIFO CMD
screen -mc "$conf"
To use for instance as:
that-script 'ls / /not-here'
The idea is that it runs screen with a temporary conf file that starts two screen windows in a vertical split layout. In the first one, we run your command with the stderr connected to the second one.
We use a named pipe for the second window to communicate its tty device to the first one, and also for the first one to tell the second one when the command is done.
The other advantage compared to pipe-based approaches is that the command's stdout and stderr are still connected to tty devices, so it doesn't affect the buffering. Both panes can also been scrolled up and down independently (using screen
's copy mode).
If you run a shell like bash
interactively with that script, you'll notice the prompt will be displayed on the second window, while the shell will read what you type in the first window as those shells output their prompt on stderr.
In the case of bash
, the echo of what you type will also appear on the second window as that echo is output by the shell (readline in the case of bash
) on stderr as well. With some other shells like ksh93
, it will show on the first window (echo output by the terminal device driver, not the shell), unless you put the shell in emacs
or vi
mode with set -o emacs
or set -o vi
.