Can zsh access the stdout of last run program?

There is no feature to capture the output from the screen on most terminal emulators. I seem to recall the author of xterm (the “reference” terminal emulator) stating that it would be difficult to implement. Even if that was possible, the shell would have to keep track of where the last prompt had been.

So you won't escape having to run the command again, unless you use a terminal-specific, manual mechanism such as copy-pasting with the mouse in xterm or with the keyboard in Screen.

It would be highly impractical for the shell to automatically capture the output of commands, because it cannot distinguish between commands that have complex terminal and user interactions from commands that simply output printable characters.

You can rerun the command and capture its output. There are various ways to do each. To rerun the command, you can use:

  • !! history substitution — most convenient to type;
  • fc -e -, which can be used in a function.

To capture the output, you can use command substitution, or a function like the following:

K () {
  lines=("${(f@)$(cat)}")
}
!! |K

This sets the lines array to the output of the command that's piped into it.


Here's a first cut of something to put the last line of output in a variable called $lastline.

precmd () {                                                                                                                                                                                                        
    exec 2>&- >&-
    lastline=$(tail -1 ~/.command.out) 
    sleep 0.1   # TODO: synchronize better
    exec > /dev/tty 2>&1
}

preexec() {
    exec > >(tee ~/.command.out&)
}

This uses zsh's preexec hook to run exec with tee to store a copy of the command's stdout, then uses precmd to read the stored output and restore stdout to be just the terminal for showing the prompt.

But it still needs some work. For example, since stdout is no longer a terminal, programs such as vim and less don't work properly.

There's some useful related information in these questions:

  • Can I configure my shell to print STDERR and STDOUT in different colors?
  • Show only stderr on screen but write both stdout and stderr to file

You can do this by just piping your results to tee, which just saves the output to a file and displays it at the same time.

So for example, you could do this, which shows your output like normal, but also saves it to the file /tmp/it

locate foobar.mmpz | tee /tmp/it

then cat that file and grep it to select things, e.g.

cat /tmp/it | grep progo | grep lms

then to use it, you could just do this:

vi $(!!)