Running bash script from macro

Hello,
I am wondering if there is a way to execute bash scripts (.sh) from a macro?

I may enhance the question like; “how can I execute a bash script from a javascript, within a macro?”

Dear @OntheWay,

the following ImageJ1 macro will execute the JavaScript script that is passed as the second argument to the eval function.

eval("script","IJ.getInstance().setAlwaysOnTop(true);")

If you want to execute native commands it might be best to use ImageJ1’s built-in exec function instead of doing that from JavaScript. See https://imagej.nih.gov/ij/macros/ExecExamples.txt for examples of how to use it.

Best,
Stefan

Dear @stelfrich,

Thank you for the pointers, I saw that example too, but the bash script I want to contain in the javascript (or any other script type that ImageJ can handle) contains some loops (for, do …) and some other commands like sed and curl. Also there are variable definitions ($VARIABLE etc…). The reason I want to integrate the bash script to ImageJ framework is; I want to feed some variables to script, in a friendly way (like paths to folders, and some integer values).

In the examples page you kindly provided, all examples seems like single bash commands, and no room for loops, variables etc., If I dont get it wrong.

Basically I need a way to run a bash script with parameters feed from ImageJ, open for further advises.

Regards,

That’s perfectly fine. The exec function doesn’t really care about the contents of your shell script. Wrap the loops and all the calls into a shell script and store it as script-with-complicated-calls.sh file. The exec call will look something like:

exec("sh", "/path/to/script-with-complicated-calls.sh");

If you want to pass parameters (e.g. arg1 and arg2) to that shell script, use exec as following:

exec("sh", "/path/to/script-with-complicated-calls.sh", "arg1", "arg2");

I would also like to raise your attention to script parameters which might make the user interface development for your task less involved:

// @String stringArgument
// @Integer integerArgument
exec("sh", "/path/to/script-with-complicated-calls.sh", stringArgument, integerArgument);
4 Likes

@stelfrich Thanks for clarification and additional info you provided. My “script-with-complicated-calls.sh” worked perfectly well, in a single line and I was very happy, until I realize one of the lines in the script totally skipped;

python2 somefile.py inputfile.txt outputfile.txt

The script works well when I run it from terminal (bash somefile.sh)

May I hope for anything?

Although I am not familiar with the implementation of exec I suspect that you issue has to do with the $PATH not being set properly for the user who executes the shell script.

Why don’t you check the absolute path to python2 via

which python2

in the bash and replace python2 with that absolute path in script-with-complicated-calls.sh?

1 Like

@stelfrich Thank you very much for your kind efforts, but it seems the python line directly skipped. I put the following line to myscript.sh and applied exec from ImageJ, nothing has happened.

/usr/bin/python2 --version

At that point, I decided to move python execution into imageJ macro, tried the following;

exec("python", "/path/to/thepythonscript.py")

It returns a response in log window about usage (USAGE: thepythonscript.py infile, outfile etc…) but this time, I couldnt find how to feed required parameters, in that line.
I tried;
exec("python", "/path/to/thepythonscript.py infile.txt outfile.txt")
or;
exec("python", "/path/to/thepythonscript.py /path/to/infile.txt /path/to/outfile.txt")

both failed…

Ok parameter problem solved, exec (“python”, “thescript.py”, “parameter1”, “parameter2”) makes it.

But I still want to keep python line in bash script, if its possible.

@Bio7 do you have any idea why a python line contained in a bash script which exec’ed from imageJ fails to work? Thanks!

Actually, it does work for me when I execute test.ijm from the script editor. It prints someString to ImageJ’s log window. Does that setup work for you?

test.ijm:

// test.ijm
exec("sh", "/home/stefan/Documents/PythonTest/test.sh");

test.sh:

#!/bin/bash
python script.py someString

script.py:

#!/usr/bin/python
import sys
print(sys.argv[1]);
1 Like

@stelfrich Now this is interesting… I tried whats available above, and nothing happened (nothing come out in Log window). In my case, exec seems like responding a certain list of commands and skips the rest. I have tried some other commands in the bash script (test.sh), like; python --version or java -version to see something, no way… :frowning:

My OS is Ubuntu 14.04.

Fiji version is 1.51K, as in about box, which also reports java 1.8.0_66 (64-bit).

But from terminal:

$ java -version
java version "1.7.0_121"
OpenJDK Runtime Environment (IcedTea 2.6.8) (7u121-2.6.8-1ubuntu0.14.04.1)
OpenJDK 64-Bit Server VM (build 24.121-b00, mixed mode)

Can you please mention about your SW versions? Maybe thats the problem… Also wondering whether you are using original ImageJ or Fiji ?

I am on Ubuntu 16.04 running an up-to-date Fiji (ImageJ1 1.51k, ImageJ2 RC57) with bundled JRE.

Also, now that I have restarted Fiji it doesn’t work anymore :weary: I’ll investigate…

@stelfrich Thank you for your interest. In my current work, job is done in three major steps and since this python execution is the second step in the middle, and cant be completed in bash script, users have to switch back and forth between imageJ and bash terminal. It will be fantastic if this problem can be handled, everything will be solved within single imageJ macro, therefore I am impatiently waiting for a solution. Also, should we declare a ‘bug’ for this?
Many thanks again.

Note that version 1.51k is the version of the bundled ImageJ 1.x, not the version of Fiji itself. Actually, at the moment, Fiji does not advertise its version explicitly—the status bar only shows the version of ImageJ2, followed by the version of ImageJ 1.x.

The exec command eats the stderr stream. The bash script is executed successfully; the problem is that the python line returns an error code (because it cannot find the Python script; see below). But bash scripts continue after errors, unless you tell them to do otherwise.

If you change the bash script to:

#!/bin/bash
echo "== Starting python =="
python script.py Quick brown fox
echo "== Python complete =="

Then you will see that both “Starting python” and “Python complete” get echoed. If you then change it again:

#!/bin/bash
set -e
echo "== Starting python =="
python script.py Quick brown fox
echo "== Python complete =="

You will see that the “Python complete” line is no longer printed, because the set -e fails the script once something goes wrong.

If you change the script a third time:

#!/bin/bash
set -e
echo "== Starting python =="
dir=$(cd "$(dirname "$0")" && pwd)
python "$dir/script.py" Quick brown fox
echo "== Python complete =="

You will now finally see that python is being executed; it’s just that the path to script.py needs to be set, because the CWD did not match the bash script directory.

Here are the contents of configuration that works for me:

$ head *
==> script.py <==
#!/usr/bin/python
import sys
print("~~~ WELCOME! TO PYTHON ~~~")
print(sys.argv[1]);
print("~~~ NOW LEAVING PYTHON ~~~")

==> test.ijm <==
// test.ijm
result = exec("sh", "/Users/curtis/odrive/Box - UW/support/forum/4819/test.sh");
print("--> EXECUTION RESULTS <--");
print(result);
print("--> HOPE YOU LIKED IT <--");

==> test.sh <==
#!/bin/bash
set -e
echo "== Starting python =="
dir=$(cd "$(dirname "$0")" && pwd)
python "$dir/script.py" Quick brown fox
echo "== Python complete =="

You will of course need to change the path in test.ijm to match the directory of your ImageJ macro.

3 Likes

@ctrueden Thank you very much for detailed information, It worked flawlessly. I believe many people will benefit from this, since I was able to find very little info about usage details of exec function.

2 Likes