One of the main reasons why the output of commands is not captured is because the output can be arbitrarily large - many megabytes at a time. Granted, not always that large, but big outputs cause problems.
–
Jonathan LefflerMay 26 '11 at 4:06

21 Answers
21

This is a really hacky solution, but it seems to mostly work some of the time. During testing, I noted it sometimes didn't work very well when getting a ^C on the command line, though I did tweak it a bit to behave a bit better.

This hack is an interactive mode hack only, and I am pretty confident that I would not recommend it to anyone. Background commands are likely to cause even less defined behavior than normal. The other answers are a better way of programmatically getting at results.

Set this bash environmental variable and issues commands as desired. $LAST will usually have the output you are looking for:

startide seth> fortune
Courtship to marriage, as a very witty prologue to a very dull play.
-- William Congreve
startide seth> echo "$LAST"
Courtship to marriage, as a very witty prologue to a very dull play.
-- William Congreve

@armandino: This of course causes programs expecting to interact with a terminal on standard-out to not work as expected (more/less) or store odd things in $LAST (emacs). But I think it is about as good as you are going to get. The only other option is to use (type)script to save a copy of EVERYTHING to a file and then use PROMPT_COMMAND to check for changes since the last PROMPT_COMMAND. This will include stuff you don't want, though. I'm pretty sure you are not going to find anything closer to what you want that this, though.
–
Seth RobertsonMay 19 '11 at 20:51

I don't know of any variable that does this automatically. To do something aside from just copy-pasting the result, you can re-run whatever you just did, eg

vim $(!!)

Where !! is history expansion meaning 'the previous command'.

If you expect there to be a single filename with spaces or other characters in it that might prevent proper argument parsing, quote the result (vim "$(!!)"). Leaving it unquoted will allow multiple files to be opened at once as long as they don't include spaces or other shell parsing tokens.

Copy-pasting is what I usually do in such a case, also because you typically need only a part of the output of the command. I'm surprised you are the first one to mention it.
–
Bruno De FraineMay 23 '11 at 7:50

1

You can do much the same with fewer keystrokes with: vim `!!`
–
psmearsMar 2 at 20:09

Bash is kind of an ugly language. Yes, you can assign the output to variable

MY_VAR="$(find -name foo.txt)"
echo "$MY_VAR"

But better hope your hardest that find only returned one result and that that result didn't have any "odd" characters in it, like carriage returns or line feeds, as they will be silently modified when assigned to a Bash variable.

But better be careful to quote your variable correctly when using it!

It's better to act on the file directly, e.g. with find's -execdir (consult the manual).

When the old-style backquoted form of substitution is used, backslash retains its literal meaning except when followed by "$", "`", or "\". The first backticks not preceded by a backslash terminates the command substitution. When using the "$(COMMAND)" form, all characters between the parentheses make up the command; none are treated specially.

EDIT: After the edit in the question, it seems that this is not the thing that the OP is looking for. As far as I know, there is no special variable like $_ for the output of last command.

Update: an anonymous user suggested to replace echo by printf '%s\n' which has the advantage that it doesn't process options like -e in the grabbed text. So, if you expect or experience such peculiarities, consider this suggestion. Another option is to use cat <<<$grab instead.

Quoting from man bash: "Here Strings: A variant of here documents, the format is: <<<word The word is expanded and supplied to the command on its standard input." So, the value of $grab is fed to cat on stdin, and cat just feeds it back to stdout.
–
Tilman VogelJan 26 at 12:56

When running an interactive shell in tmux, you can easily access the data currently displayed on a terminal. Let's take a look at some interesting commands:

tmux capture-pane: this one copies the displayed data to one of the tmux's internal buffers. It can copy the history that's currently not visible, but we're not interested in that now

tmux list-buffers: this displays the info about the captured buffers. The newest one will have the number 0.

tmux show-buffer -b (buffer num): this prints the contents of the given buffer on a terminal

tmux paste-buffer -b (buffer num): this pastes the contents of the given buffer as input

Yeah, this gives us a lot of possibilities now :) As for me, I set up a simple alias: alias L="tmux capture-pane; tmux showb -b 0 | tail -n 3 | head -n 1" and now every time I need to access the last line i simply use $(L) to get it.

This is independent of the output stream the program uses (be it stdin or stderr), the printing method (ncurses, etc.) and the program's exit code - the data just needs to be displayed.

If all you want is to rerun your last command and get the output, a simple bash variable would work:

LAST=`!!`

So then you can run your command on the output with:

yourCommand $LAST

This will spawn a new process and rerun your command, then give you the output. It sounds like what you would really like would be a bash history file for command output. This means you will need to capture the output that bash sends to your terminal. You could write something to watch the /dev or /proc necessary, but that's messy. You could also just create a "special pipe" between your term and bash with a tee command in the middle which redirects to your output file.

But both of those are kind of hacky solutions. I think the best thing would be terminator which is a more modern terminal with output logging. Just check your log file for the results of the last command. A bash variable similar to the above would make this even simpler.

This command deals with multiple files and works like a charm even if the path and/or filename contains space(s).

Notice the mv {} /some/new/location/{} part of the command. This command is build and executed for each line printed by earlier command. Here the line printed by earlier command is replaced in place of {}.

So, the actual magic happens with sed, you pipe what ever output of what ever command into sed and sed prints the last row which you can use as parameter with back ticks. Or you can combine that to xargs also. ("man xargs" in shell is your friend)