Call a Mathematica program from the command line, with command-line args, stdin, stdout, and stderr - command-line

If you have Mathematica code in foo.m, Mathematica can be invoked with -noprompt
and with -initfile foo.m
(or -run "<<foo.m")
and the command line arguments are available in $CommandLine (with extra junk in there) but is there a way to just have some mathematica code like
#!/usr/bin/env MathKernel
x = 2+2;
Print[x];
Print["There were ", Length[ARGV], " args passed in on the command line."];
linesFromStdin = readList[];
etc.
and chmod it executable and run it? In other words, how does one use Mathematica like any other scripting language (Perl, Python, Ruby, etc)?

MASH -- Mathematica Scripting Hack -- will do this.
Since Mathematica version 6, the following perl script suffices:
http://ai.eecs.umich.edu/people/dreeves/mash/mash.pl
For previous Mathematica versions, a C program is needed:
http://ai.eecs.umich.edu/people/dreeves/mash/pre6
UPDATE: At long last, Mathematica 8 supports this natively with the "-script" command-line option:
http://www.wolfram.com/mathematica/new-in-8/mathematica-shell-scripts/

Here is a solution that does not require an additional helper script. You can use the following shebang to directly invoke the Mathematica kernel:
#!/bin/sh
exec <"$0" || exit; read; read; exec /usr/local/bin/math -noprompt "$#" | sed '/^$/d'; exit
(* Mathematica code starts here *)
x = 2+2;
Print[x];
The shebang code skips the first two lines of the script and feeds the rest to the Mathematica kernel as standard input. The sed command drops empty lines produced by the kernel.
This hack is not as versatile as MASH. Because the Mathematica code is read from stdin you cannot use stdin for user input, i.e., the functions Input and InputString do not work.

Assuming you add the Mathematica binaries to the PATH environment variable in ~/.profile,
export PATH=$PATH:/Applications/Mathematica.app/Contents/MacOS
Then you just write this shebang line in your Mathematica scripts.
#!/usr/bin/env MathKernel -script
Now you can dot-slash your scripts.
$ cat hello.ma
#!/usr/bin/env MathKernel -script
Print["Hello World!"]
$ chmod a+x hello.ma
$ ./hello.ma
"Hello World!"
Tested with Mathematica 8.0.
Minor bug: Mathematica surrounds Print[s] with quotes in Windows and Mac OS X, but not Linux. WTF?

Try
-initfile filename
And put the exit command into your program

I found another solution that worked for me.
Save the code in a .m file, then run it like this: MathKernel -noprompt -run “<
This is the link: http://bergmanlab.smith.man.ac.uk/?p=38

For mathematica 7
$ cat test.m
#!/bin/bash
MathKernel -noprompt -run < <( cat $0| sed -e '1,4d' ) | sed '1d'
exit 0
### code start Here ... ###
Print["Hello World!"]
X=7
X*5
Usage:
$ chmod +x test.m
$ ./test.m
"Hello World!"
7
35

Related

Cross-platform compatible sed command?

I'm trying to use sed, but I want to run properly both under Linux and Mac. Currently, I have something like this:
if test -f ${GENESISFILE};
then
echo "Replacing ..."
sed -i '' "s/ADDRESS/${ADDRESS}/g" ${GENESISFILE}
else
echo "No such file"
fi
Now, the point is that using -i '' part it runs properly under Mac, but doesn't under Linux, and if I remove it then it doesn't work under Mac. What's proper way to make it cross-platform compatible?
Instead of sed one-liner:
sed -i '' "s/ADDRESS/${ADDRESS}/g" ${GENESISFILE}
use this cross-platform Perl one-liner, which runs OK on both Linux and macOS:
perl -i.bak -pe 's/ADDRESS/$ENV{ADDRESS}/g' ${GENESISFILE}
The Perl one-liner uses these command line flags:
-e : Tells Perl to look for code in-line, instead of in a file.
-p : Loop over the input one line at a time, assigning it to $_ by default. Add print $_ after each loop iteration.
-i.bak : Edit input files in-place (overwrite the input file). Before overwriting, save a backup copy of the original file by appending to its name the extension .bak. Use -i alone, without .bak, to skip making the backup.
SEE ALSO:
perldoc perlrun: how to execute the Perl interpreter: command line switches

A change of shebang + eval leads to the perl script failure

This is a question derives from another post. Based upon the comments and answer from that post, I change the following shebang + eval into another one:
Old works version
#!/bin/perl
eval 'exec perl5 -S $0 ${1+"$#"}'
if 0;
New doesn't work version
#!/bin/sh
eval 'exec perl5 -S $0 ${1+"$#"}'
if 0;
Notice that I change #!/bin/perl to #!/bin/sh and based upon my understanding, the new version should also work because the script is treated like shell script and eval get executed and perl5 is invoked to use perl to execute the same script. However, when I actually run this, I got:
/bin/sh: -S: invalid option
Can anyone explain why this case the script is failed. Do I misunderstand something? I'm using ksh
Also this web page I found online seems suggest that my new version should work as well.
Thanks much!
From the Perl documentation:
If the #! line does not contain the word "perl" nor the word "indir", the program named after the #! is executed instead of the Perl interpreter. This is slightly bizarre, but it helps people on machines that don't do #!, because they can tell a program that their SHELL is /usr/bin/perl, and Perl will then dispatch the program to the correct interpreter for them.
So if you launch the modified version as ./script:
Your shell executes ./script
The kernel actually executes /bin/sh ./script
sh executes perl5 -S ./script
perl5 executes /bin/sh -S ./script because it sees a shebang that doesn't contain perl.
sh dies because it doesn't recognize the -S option.
And if you launch the modified version as perl5 script:
Your shell executes perl5 script
perl5 executes /bin/sh -S script because it sees a shebang that doesn't contain perl.
sh dies because it doesn't recognize the -S option.
Also this web page I found online seems suggest that my new version should work as well.
The code on that page is significantly different than the code you used. In that code, there's an explicit instruction (-x) to ignore the actual shebang line, and to look for one that contains perl later in the file (which is also missing from your code).

How to check if a Perl script doesn't have any compilation errors?

I am calling many Perl scripts in my Bash script (sometimes from csh also).
At the start of the Bash script I want to put a test which checks if all the Perl scripts are devoid of any compilation errors.
One way of doing this would be to actually call the Perl script from the Bash script and grep for "compilation error" in the piped log file, but this becomes messy as different Perl scripts are called at different points in the code, so I want to do this at the very start of the Bash script.
Is there a way to check if the Perl script has no compilation error?
Beware!!
Using the below command to check compilation errors in your Perl program can be dangerous.
$ perl -c yourperlprogram
Randal has written a very nice article on this topic which you should check out
Sanity-checking your Perl code (Linux Magazine Column 91, Mar 2007)
Quoting from his article:
Probably the simplest thing we can tell is "is it valid?". For this,
we invoke perl itself, passing the compile-only switch:
perl -c ourprogram
For this operation, perl compiles the program,
but stops just short of the execution phase. This means that every
part of the program text is translated into the internal data
structure that represents the working program, but we haven't actually
executed any code. If there are any syntax errors, we're informed, and
the compilation aborts.
Actually, that's a bit of a lie. Thanks to BEGIN blocks (including
their layered-on cousin, the use directive), some Perl code may have
been executed during this theoretically safe "syntax check". For
example, if your code contains:
BEGIN { warn "Hello, world!\n" }
then you will see that message,
even during perl -c! This is somewhat surprising to people who
consider "compile only" to mean "executes no code". Consider the
code that contains:
BEGIN { system "rm", "-rf", "/" }
and you'll see the problem with
that argument. Oops.
Apart from perl -c program.pl, it's also better to find warnings using the command:
perl -w program.pl
For details see: http://www.perl.com/pub/2004/08/09/commandline.html
I use the following part of a bash func for larger perl projects :
# foreach perl app in the src/perl dir
while read -r dir ; do
echo -e "\n"
echo "start compiling $dir ..." ;
cd $product_instance_dir/src/perl/$dir ;
# run the autoloader utility
find . -name '*.pm' -exec perl -MAutoSplit -e 'autosplit($ARGV[0], $ARGV[1], 0, 1, 1)' {} \;
# foreach perl file check the syntax by setting the correct INC dirs
while read -r file ; do
perl -MCarp::Always -I `pwd` -I `pwd`/lib -wc "$file"
# run the perltidy inline
# perltidy -b "$file"
# sleep 3
ret=$? ;
test $ret -ne 0 && break 2 ;
done < <(find "." -type f \( -name "*.pl" -or -name "*.pm" \))
test $ret -ne 0 && break ;
echo "stop compiling $dir ..." ;
echo -e "\n\n"
cd $product_instance_dir ;
done < <(ls -1 "src/perl")
When you need to check errors/warnings before running but your file depends on mutliple other files you can add option -I:
perl -I /path/to/dependency/lib -c /path/to/file/to/check
Edit: from man perlrun
Directories specified by -I are prepended to the search path for modules (#INC).

Cannot execute system command in cygwin

I have the following simple perl script that I cannot execute in cygwin:
#!/usr/bin/perl
use strict;
system("../cat.exe < a.txt > b.txt");
When I run it, the script tells me:
./my_test.pl
'..' is not recognized as an internal or external command,
operable program or batch file.
However I can run the command in the cygwin shell:
$ ../cat.exe < a.txt > b.txt
$ ../cat.exe b.txt
hello
The executable cat.exe exists in the directory above and a.txt in the current working
directory.
My version of perl:
$ perl -v
This is perl, v5.8.8 built for MSWin32-x86-multi-thread
(with 12 registered patches, see perl -V for more detail)
You're using a perl built for Windows (ActiveState? Strawberry?), not the Cygwin version. It invokes cmd.exe for system(), which thinks that .. is the command and / introduces an option.
Try changing the the system() call to:
system("..\\cat.exe < a.txt > b.txt");
But you should normally be using the Cygwin version of perl when running a script from bash.
What is the output of the following commands?
echo "$PATH"
type -a perl
/usr/bin/perl -v
From what we've seen so far, it looks like you've installed some Windows-specific Perl with its perl.exe in your Cygwin /usr/bin directory. If so, then (a) uninstall it (you can reinstall it elsewhere if you like), and (b) re-install the "perl" package via Cygwin's setup.exe.
(And add use warnings; after use strict; in your Perl scripts. This isn't related to your problem, but it's good practice.)
The error message obviously comes from cmd.exe, which apparently is your default shell. What does echo $SHELL say? Maybe you need to define that variable to become /bin/bash.exe.

Sed on AIX does not recognize -i flag

Does sed -i work on AIX?
If not, how can I edit a file "in place" on AIX?
The -i option is a GNU (non-standard) extension to the sed command. It was not part of the classic interface to sed.
You can't edit in situ directly on AIX. You have to do the equivalent of:
sed 's/this/that/' infile > tmp.$$
mv tmp.$$ infile
You can only process one file at a time like this, whereas the -i option permits you to achieve the result for each of many files in its argument list. The -i option simply packages this sequence of events. It is undoubtedly useful, but it is not standard.
If you script this, you need to consider what happens if the command is interrupted; in particular, you do not want to leave temporary files around. This leads to something like:
tmp=tmp.$$ # Or an alternative mechanism for generating a temporary file name
for file in "$#"
do
trap "rm -f $tmp; exit 1" 0 1 2 3 13 15
sed 's/this/that/' $file > $tmp
trap "" 0 1 2 3 13 15
mv $tmp $file
done
This removes the temporary file if a signal (HUP, INT, QUIT, PIPE or TERM) occurs while sed is running. Once the sed is complete, it ignores the signals while the mv occurs.
You can still enhance this by doing things such as creating the temporary file in the same directory as the source file, instead of potentially making the file in a wholly different file system.
The other enhancement is to allow the command (sed 's/this/that' in the example) to be specified on the command line. That gets trickier!
You could look up the overwrite (shell) command that Kernighan and Pike describe in their classic book 'The UNIX Programming Environment'.
#!/bin/ksh
host_name=$1
perl -pi -e "s/#workerid#/$host_name/g" test.conf
Above will replace #workerid# to $host_name inside test.conf
You can simply install GNU version of Unix commands on AIX :
http://www-03.ibm.com/systems/power/software/aix/linux/toolbox/alpha.html
You can use a here construction with vi:
vi file >/dev/null 2>&1 <<#
:1,$ s/old/new/g
:wq
#
When you want to do things in the vi-edit mode, you will need an ESC.
For an ESC press CTRL-V ESC.
When you use this in a non-interactive mode, vi can complain about the TERM not set. The solution is adding export TERM=vt100 before calling vi.
Another option is to use good old ed, like this:
ed fileToModify <<EOF
,s/^ff/gg/
w
q
EOF
you can use perl to do it :
perl -p -i.bak -e 's/old/new/g' test.txt
is going to create a .bak file.