Extending the last output script

As threatened at the end of my last post, I’ve extended the script for putting the output of the last command in Terminal to handle Octave and Python sessions as well as bash sessions. I often need to include results from these sessions in reports and other documents, and a quick keyboard command is faster and more accurate than clicking and dragging, especially when I’m working on my MacBook Air and using a trackpad.

Like the simpler script that handled bash only, the extended version does the following:

  1. Splits the history of the current Terminal tab on the command prompt.
  2. Grabs the item that contains both the input and output of the last command.
  3. Eliminates the input.
  4. Puts the result on the clipboard.

To handle Python and Octave in addition to bash, the script needs to know which interpreter is running and the prompt that interpreter uses.

On my system, the titlebar of the Terminal window displays, among other things, the name of the program being executed. This is set by checking the “Active process name” item in the Window settings.

Terminal window settings

A quick look through the AppleScript dictionary for Terminal shows that the name property of a window contains the window’s title. We’ll use that to check for “octave,” “python,” or “bash.”

As for the prompts, we have to be careful because both Python and Octave have two types of input prompts: the primary prompt and the secondary (or continuation) prompt. Commands always start after the primary prompt. If the command extends over more than one line—as with loops and branches—the second and later lines are entered after the secondary prompt. Here’s an example from an interactive Python session:

>>> for i in range(1,6):
...   square.append(i**2)
...   cube.append(i**3)

By default, Python’s primary and secondary prompts are >>> and ..., respectively, each with a trailing space. As you’ll see in the script source code, to get just the output I split first on the primary prompt and then on the secondary.

Octave’s default primary prompt includes the word “octave” and the command number, which starts with 1 at the beginning of the Octave session. This is unnecessarily long and includes information of little value to me. In several years of using Octave, I’ve never once used the command number. I like Python’s prompts because the primary and secondary are the same length, which makes it easy to use conventional indentation in an interactive session, so I’ve changed Octave’s prompts to match Python’s. Adding these lines to my ~/.octaverc file makes the changes.

PS1(">>> ")
PS2("... ")

With that in mind, here’s the revised code for “Copy Last Output:”

 1:  #!/usr/bin/python
 3:  from appscript import *
 4:  from osax import OSAX
 6:  sa = OSAX()
 7:  win = app('/Applications/Utilities/Terminal').windows[1]
 8:  title = win.name.get().lower()
10:  if title.find("bash") > -1:
11:    inout = win.selected_tab.history.get().split('\n$ ')[-2]
12:    sa.set_the_clipboard_to('\n'.join(inout.split('\n')[1:-1]))
13:  elif title.find("python") > -1 or title.find("octave") > -1:
14:    inout = win.selected_tab.history.get().split('\n>>> ')[-2]
15:    sa.set_the_clipboard_to('\n'.join(inout.split('\n... ')[-1].split('\n')[1:]))

If you want to use this or modify it for your own purposes, you’ll have to have the appscript library installed.

Line 8 gets the title of the current window, and the if/elif structure tests for which interpreter is running. Octave and Python are handled by the same code because I have them set to use the same prompts.

This has replaced my earlier, simpler script. I have FastScripts set up to run this command when Terminal is the active application and I press ⌃⌥⌘C.