Linux – Is it possible to save Linux virtual console content and scrollback in a file

consolelinux

I have a long-term running script and I forgot to redirect its output to a file. I can see it in a terminal, but can I save it to a file?

I'm not asking for tee, output redirection (e.g. >, >>) etc – the command has started, and I can't run it again. I need to save already generated output. If I can see it on my display, it is somewhere stored/cached/buffered. Where?

screendump, /dev/vcsX and so on allows me to save only last screen on terminal output (not the current! – scrolling terminal doesn't help).

This is on a Linux virtual console, not a X11 terminal emulator like gnome-terminal with mouse and other goodies.

Best Answer

/dev/vcs[a]<n> will only get you the last screen-full even if you've scrolled up, but the selection ioctl()s as used by gpm will allow you to dump the currently displayed screen even when you've scrolled up.

So you can can do:

sleep 3; perl -e '
  require "sys/ioctl.ph";
  # copy:
  ioctl(STDIN, &TIOCLINUX, $arg = pack("CS5", 2, 1, 1, 80, 25, 2));
  # paste:
  ioctl(STDIN, &TIOCLINUX, $arg = "\3")'; cat > file

Adjust the 80 and 25 to your actual screen width and height.

The sleep 3 gives you time to scroll up (with Shift+PageUP) to the actual screen you want to dump. cat > file redirects the paste to file. Finish it with Ctrl+D.

See console_ioctl(4) for details.

If you have gpm installed and running, you can do that selection with the mouse.

The Linux virtual console scrollback and selection are very limited and quite annoying (in that when you switch console, you lose the whole scrollback). Going forward, I'd suggest you use things like GNU screen or tmux within it (I personally use them in even more capable terminals). With them, you can have larger searchable scrollbacks and easily dump them to files (and even log all the terminal output, plus all the other goodies that come with those terminal multiplexers).


As to automating the process to dump the whole scrollback buffer, it should be possible under some conditions, but quite difficult as the API is very limited. There is an undocumented ioctl (TIOCLINUX, subcode=13) to scroll the current virtual console by some offset (negative for scrolling up, positive for scrolling down).

There is however no way (that I know) to know the current size of the scrollback buffer. So it's hard to know when you've reached the top of that buffer. If you attempt to scroll past it, the screen will not be shifted by as much and there's no reliable way to know by how much the screen has actually scrolled.

I also find the behaviour of the scrolling ioctl erratic (at least with the VGA console), where scrolling by less than 4 lines works only occasionally.

The script below seems to work for me on frame buffer consoles (and occasionally on VGA ones) provided the scrollback buffer doesn't contain sequences of identical lines longer than one screen plus one line.

It's quite slow because it scrolls one line at a time, and needs to wait 10ms for eof when reading each screen dump.

To be used as that-script > file from within the virtual console.

#! /usr/bin/perl
require "sys/ioctl.ph";
($rows,$cols) = split " ", `stty size`;
$stty = `stty -g`; chomp $stty;
system(qw(stty raw -echo icrnl min 0 time 1));

sub scroll {
  ioctl(STDIN, &TIOCLINUX, $arg = pack("Cx3l", 13, $_[0])) or die "scroll: $!";
}
sub grab {
  ioctl(STDIN, &TIOCLINUX, $arg = pack("CS5", 2, 1, 1, $cols, $rows, 2)) or die "copy: $!";
  ioctl(STDIN, &TIOCLINUX, $arg = "\3") or die "paste: $!";
  return <STDIN>;
}
for ($s = 0;;$s--) {
  scroll $s if $s;
  @lines = grab;
  if ($s) {
    last if "@lines" eq "@lastlines";
    unshift @output, $lines[0];
  } else {
    @output = @lines;
  }
  @lastlines = @lines;
}
print @output;
exec("stty", $stty);