Take the 2-minute tour ×
Mathematica Stack Exchange is a question and answer site for users of Mathematica. It's 100% free, no registration required.

I love how notebooks work in Mathematica.

You can edit code in real time and hit Ctrl+Shift to run it.

Additionally you copy and paste different cells around to organize and test ideas.

Search and replace, plot/visualization,etc.. are built into the system.

My question: How could you adequately adapt Mathematica to use as a terminal replacement?

If not (this might get transferred to stackoverflow.com) is there a terminal editor that exists that works like Mathematica's notebook interface?

share|improve this question
    
You can call external programs with Run[] and RunThrough[] but I don't think they can work interactively. –  0xFE Jan 27 '13 at 7:25
1  
In response to your second question, the BBEdit shell worksheet offers some broadly similar ideas. –  cormullion Jan 27 '13 at 9:20
1  
Any version of Emacs has full terminal capability, and you find a version (often several) for any OS I know of. –  m_goldberg Jan 27 '13 at 9:55
add comment

1 Answer

One thing that we can do is exploit the CellEvaluationFunction option of cells to replace the standard evaluator with a function of our own design. In this case, that evaluation function will invoke the system shell.

First, let's create a helper function that can create such cells:

evaluatableCell[label_String, evaluationFunction_] :=
  ( CellPrint[
      TextCell[
        ""
      , "Program"
      , Evaluatable -> True
      , CellEvaluationFunction -> evaluationFunction
      , CellFrameLabels -> {{None, label}, {None, None}}
      , CellGroupingRules -> "InputGrouping"
      ]
    ]
  ; SelectionMove[EvaluationNotebook[], All, EvaluationCell]
  ; NotebookDelete[]
  ; SelectionMove[EvaluationNotebook[], Next, CellContents]
  )

This function creates a cell with the look of a Program cell, but evaluates its contents using a function we provide. So if we wanted to have the system shell evaluate the contents, we would do this:

evaluatableCell["shell", Import["!"~~#, "Text"]&]

Note that when we evaluate this expression, the input expression is erased and replaced with a new cell. I'll say that again: the original expression is erased. This is convenient for frequent use, but if you have typed in a complex expression for testing purposes, save it before you evaluate! If you don't like this behaviour, simply remove the SelectionMove and NotebookDelete lines from evaluatableCell.

The replacement cell is treated as an evaluatable input cell. The following example was run on Windows:

evaluatableCell

This shell cell is very basic. The error-handling is non-existent and, on Windows at least, ignores all lines after the first. We can make this a little more robust if we write the text to be evaluated into a script file, run it, and capture the output. The following helper function captures this common idiom:

Options[runTempFile] = {FilePattern -> "_", FileExtension -> Automatic};
runTempFile[command_, text_, OptionsPattern[]] :=
  Module[{stream, file, result}
  , stream = OpenWrite[]
  ; Export[stream, text, "Text"]
  ; file = Close[stream]
  ; OptionValue[FileExtension] /.
      ext:Except[Automatic] :> (file = RenameFile[file, file~~"."~~ext])
  ; result = Import[
      StringReplace[command, OptionValue[FilePattern] -> file]
    , "Text"
    ]
  ; DeleteFile[file]
  ; result
  ]

Error-recovery is still a bit lax in this example, but we'll ignore that and blunder on. Armed with this helper function, we can handle the Windows limitation a bit better:

shellEvaluate[cmd_] /; $OperatingSystem == "Windows" :=
  runTempFile["!_ 2>&1", "@echo off\n"~~cmd, FileExtension -> "cmd"]

shellEvaluate[cmd_] :=
  Import["!"~~cmd~~" 2>&1", "Text"]

shellCell[] := EvaluatableCell["Shell", shellEvaluate[#]&]

Note that we are now merging the standard output and standard error streams. Let's use shellCell:

shellCell[]

shellCell

So we have met our stated goal of being able to execute shell commands from within Mathematica. But why stop there? All the arguments for being able to intersperse shell commands into our notebooks apply to any other language we use. Maybe we have Haskell installed on our machine:

haskellEvaluate[source_] := runTempFile["!runhaskell _ 2>&1", source]

haskellCell[] :=
  EvaluatableCell["Haskell", haskellEvaluate[#]&]

then:

haskellCell[]

haskell

Mathematica's built-in Java also opens up some interesting possibilities. The included Rhino Javascript interpreter, for example:

Needs["JLink`"]
InstallJava[];

evaluateRhino[script_String] :=
  JavaBlock @ Module[{engine}
  , engine = JavaNew["javax.script.ScriptEngineManager"]@getEngineByName["JavaScript"]
  ; engine@eval[script]
  ]

rhinoCell[] :=
  evaluatableCell["Rhino", evaluateRhino[#]&]

Like this:

rhinoCell[]

rhino

I could go on with more examples, but I think the point is made.

For an example of a user-defined input cell type that remembers state between evaluations, see this question.

Once again, I'll point out that the error-handling presented here is weak. I omitted it in order to keep this posting to a manageable size and to avoid drowning out the key ideas with boilerplate code.

share|improve this answer
    
Why do you use ~~ in evaluatableCell["shell", Import["!" ~~ #, "Text"] &] opposed to evaluatableCell["shell", Import["!" <> #, "Text"] &] ? Is this simply a style question or is their a another reason? –  Liam William Jan 31 '13 at 1:03
    
@Lime There is no deep reason for my choice... until you mentioned it I hadn't really noticed that I had switched my common usage from <> to ~~. –  WReach Jan 31 '13 at 1:17
    
Somehow I missed this version of your answer. Big +1. –  Leonid Shifrin Jul 25 '13 at 14:53
    
Yes, I also only saw the previous version :). –  Jacob Akkerboom Jul 25 '13 at 15:03
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.