12

Is it possible to utilize all three: command line arguments, stdin, and stdout all at once in script for Windows? Unix is fairly easy, but I can't get the following to work with Windows.

Using the following as reference args and stdin. I would like the following output.

_ FileNames[] | _ ToLowerCase@in
William
  • 7,595
  • 2
  • 22
  • 70

3 Answers3

10

On Windows, MathKernel.exe launches the kernel running within a window. The standard output is redirected to that window. To avoid this redirection, we must run the kernel as a console application using Math.exe instead:

 "C:\Program Files\Wolfram Research\Mathematica\10.0\Math" ^
   -noprompt ^
   -script C:\Users\a\Desktop\test.m ^
   "data goes here"

Version 10 also has wolfram.exe which does the same thing.


Update: Using DOSKEY

As requested in a comment, we can define a Windows command alias for our script like this:

doskey _="C:\Program Files\Wolfram Research\Mathematica\10.0\Math" -noprompt -script "C:\path\to\script.m" $*

If script.m contains this...

Print @ {"input" -> InputString[], "cmdline" -> $CommandLine}

... then we can have the following exchange on the command line...

C:\> _ arg1 arg2
abcde

... which produces this output:

{"input" -> "abcde", "cmdline" -> {"C:\\Program Files\\Wolfram Research\\Mathematica\\10.0\\Math",
 "-noprompt", "-script", "C:\\path\\to\\script.m", "arg1", "arg2"}}

Update 2: Using a Command File

We can use a Windows command file to emulate the CYGWIN-based pattern established in the answer by @William. First, we create a command file named _.cmd that contains the following lines:

@echo off
"C:\Program Files\Wolfram Research\Mathematica\10.0\Math" ^
  -noprompt -script "%~dp0mmascript.m" %*

Ensure that _.cmd is in a directory that appears on the Windows executable PATH. The current working directory is fine.

Next, in the same directory, create a Mathematica script named mmascript.m with the following commands:

in := ToExpression @ InputString[]
If[Length @ $CommandLine < 5
, Print["no args"]
, Print @ ToExpression @ # & /@ $CommandLine[[5;;]]
]

It only needs to be in the same directory because the command file uses %~dp0 to reference it -- feel free to change that to an absolute pathname if desired.

mmascript.m differs from @William's test.m in that it evaluates all user-supplied arguments instead of just the first.

Here is a session transcript of the command file in action:

C:\Users\wreach> _
"no args"

C:\Users\wreach> _ Sin[Pi/2]
1

C:\Users\wreach> _ in
4 * 5
20

C:\Users\wreach> _ First@FileNames[]
"AppData"

C:\Users\wreach> _ FileNames[] | _ "in // First"
"AppData"

C:\Users\wreach> echo "test" | _ ToUpperCase@in
"TEST"

Note how the last two examples use pipes to feed input to the script. Like the CYGWIN example, the variable in expands to the result of evaluating a string read from stdin.

WReach
  • 68,832
  • 4
  • 164
  • 269
  • InputString[] doesn't seem to work in your example program if you attempt to assign the function to a doskey shortcut. – William Feb 15 '15 at 22:36
  • @William What was the nature of the failure? It all seemed to work for me when I tried it just now on Windows 7: stdin, stdout, command line arguments. I had to avoid breaking the command across lines since DOSKEY uses different escaping conventions than the command processor. Double-check that the defined macro takes the form you expect by inspecting DOSKEY /m. – WReach Feb 15 '15 at 23:01
  • try echo test | _ arg1 arg2 it fails in W7 because of the nature of the way doskey works. '_' is not a recognized as an internal or external command. – William Feb 16 '15 at 00:15
  • While _ arg1 arg2 \n t works. http://stackoverflow.com/questions/17074672/redirecting-stderr-and-stdout-in-a-doskey-macro – William Feb 16 '15 at 00:22
  • @William Fair enough. I've always avoided DOSKEY because it is not recognized anywhere but at the start of interactive command input. I would just use a normal cmd file (e.g. create _.cmd somewhere in the PATH). But we have now firmly left Mathematica and are well into superuser :) – WReach Feb 16 '15 at 02:33
  • If you know a fix I will gladly post a 50 point bounty. – William Feb 16 '15 at 06:07
  • @William I have added a section detailing the use of a cmd file to emulate the CYGWIN example in your response. Hopefully this version will get you closer to your goal. I have left out the pesky details concerning error handling, escaping, etc. The offer of a bounty is appreciated, but not necessary. – WReach Feb 16 '15 at 07:05
  • It appears to only support one argument. Is this intentional? – William Feb 16 '15 at 17:33
  • @William It accepts only one argument because I was following the lead of test.m. There is no essential obstacle to adjusting the Mathematica script to accept as many arguments as you like, and to evaluate them or not evaluate them as suits the application. – WReach Feb 16 '15 at 17:38
  • To clarify on your above comment. doskey accepts _ arg1 arg2 as arguments while you cmd script accepts only _ arg1 of the 2 arguments. I'm going to hold off on the bounty for now because I don't believe that is the intended behavior. – William Feb 17 '15 at 21:38
  • \.cmd_ accepts any number of arguments -- but mmascript.m only looks at argument #5 (just like test.m). To illustrate what I meant by my comment, I have changed the mmascript.m script to evaluate and print all user-supplied arguments. – WReach Feb 17 '15 at 22:25
  • Do you know of anyway of getting this to work with the already open mathematica session so you are not opening a completely new session every time you run the command? I'm not certain how to write to stdout and stderror with this issue currently. – William Aug 05 '15 at 03:53
  • 1
    @William Streams named "stdout" and "stderr" are available, e.g. WriteString["stderr", "bad news!"]. As to re-using a session using only streams and strings, the only approach that comes to mind at the moment is to implement a Telnet-like protocol where Mathematica and the other process take turns sending commands and responses back and forth. MathLink would give complete fine-grained control but would take more effort to set up. – WReach Aug 05 '15 at 14:59
2

After a lot of thinking and experimenting I have found Cygwin to be the the best option to support both multiple arguments and stdin.

alias _="/cygdrive/c/Program\ Files/Wolfram\ Research/Mathematica/10.0/Math -noprompt -script "C:/Users/a/Desktop/test.m" "$@""
alias ls="_ FileNames[]"

test.m

in:=ToExpression@InputString[];
If[Length@$CommandLine<5,
      Print["no args"],
      Print@ToExpression@$CommandLine[[5]]
]

Command line

_ FileNames[] | _ ToExpression[in][[1]]
William
  • 7,595
  • 2
  • 22
  • 70
1

Here is completely alternative solution that works similar to #! but for Windows.

@echo off
"C:\Program Files\Wolfram Research\Mathematica\10.0\Math" -noprompt -run "ToExpression@StringJoin@Riffle[Drop[StringSplit[UsingFrontEnd@Import[$CommandLine[[5]], \"Text\"],\"\n\"], 3], \"\n\"];Quit[];" %0 %*
exit

Print[$CommandLine[[6;;]]];
Pause[5];
William
  • 7,595
  • 2
  • 22
  • 70