views:

2155

answers:

3

I tried to run a shell script from java code, but I am facing problem. The script is in batchstart.sh file -

#!/bin/ksh
export DISPLAY=:0.0

Now the script is run with a dot on the command line -- . batchstart.sh

How do I run it from java? My java code is below. It throws the following exception -

java.io.IOException: .: not found
    at java.lang.UNIXProcess.forkAndExec(Native Method)
    at java.lang.UNIXProcess.<init>(UNIXProcess.java:102)
    at java.lang.ProcessImpl.start(ProcessImpl.java:65)
    at java.lang.ProcessBuilder.start(ProcessBuilder.java:451)
    at java.lang.Runtime.exec(Runtime.java:591)
    at java.lang.Runtime.exec(Runtime.java:429)
    at SetDisplay.main(SetDisplay.java:12)
import java.io.*;

public class SetDisplay {

   public static void main(String[] args) {

       File wd = new File("/myhomedir/");
       System.out.println("Working Directory: " +wd);
       Process proc = null;

       try {
           proc = Runtime.getRuntime().exec(". batchstart.sh", null, wd);
       } catch (Exception e) {
           e.printStackTrace();
       }
   }
}

How do I make the shell script run ?

I tried the following code as well, but that too doesn't work.

File wd = new File("/bin"); 
System.out.println(wd); 
Process proc = null; 
try { 
  proc = Runtime.getRuntime().exec("/bin/bash", null, wd); 
} 
catch (IOException e) { 
  e.printStackTrace(); 
} 
if (proc != null) { 
  BufferedReader in = new BufferedReader(new InputStreamReader(proc.getInputStream())); 
  PrintWriter out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(proc.getOutputStream())), true); 
  out.println("cd /home/"); 
  out.println(". batchstart.sh"); 
  out.println("exit"); 
  try { 
    String line; 
    while ((line = in.readLine()) != null) { 
      System.out.println(line); 
    } 
    proc.waitFor(); 
    in.close(); 
    out.close(); 
    proc.destroy(); 
  } 
  catch (Exception e) { 
    e.printStackTrace(); 
  } 
}
+1  A: 

The source command (".") is a shell built-in. You have to explicitly run /bin/ksh, passing your script name as the argument (followed by any script arguments).

You have a larger problem if you need to source the script. That usually means that environment changes happen in the context of the current shell, not a subshell.

This won't work with Java since Java's not a shell. You'll need to figure out how to change the environment with Java.

Of course, if I'm wrong and there's more to that script that just setting DISPLAY, it may work as suggested.

The method you're going to have to use depends on what you're trying to achieve(as in "Are you running other programs using exec() that rely on DISPLAY being set?" or "Does your Java program need DISPLAY to be set?").

If, as you state in your comment, it's only your Java program that needs DISPLAY set, just set it outside before your program runs. Create a cmd (or bash) file which sets the DISPLAY variable then calls the JRE to run your program.

#/bin/ksh
export DISPLAY-:0.0
/usr/bin/jre/java your_program blah.blah.blah

I would also modify your main() to check that it's set to something and exit gracefully if not:

if (System.getenv ("DISPLAY") == null)
    // doesn't exist, exit gracefully.
paxdiablo
My java program just needs the DISPLAY to be set. So isn't there any way that I can run the script from java?
Vicky
So you mean there isn't any way to make "export DISPLAY=:0.0" work from java?
Vicky
No, see update. Your basic problem is that anything external you run from Java will NOT affect Javas environment. And, on top of that, I don't believe Java provides a setenv().
paxdiablo
Yes, that's what I mean, Vicky (unfortunately).
paxdiablo
Do you need it set because you're calling X routines or is your Java code just using getenv() to get DISPLAY and use it?
paxdiablo
Sorry I was late to see your update. I have already tried the approach suggested by you. And it works for me, but the problem is that it breaks other applications running in the same environment. So I was looking to tightly couple the DISPLAY with my application.
Vicky
I had asked a separate question related to this. You will understand my problem if you look at it. http://stackoverflow.com/questions/523107?sort=newest#sort-top
Vicky
I understand now, but I don't know Tomcat. On WAS, you would set up separate containers each with their own environment. Can you do the same in Tomcat or is it just one 'container'? You may have to resort to a separate Tomcat server altogether for your DISPLAY-requiring app.
paxdiablo
No, when export is set in Tomcat, there is no problem at all. But when it is set on the batch server's startup file, it causes problem for other applications running on batch server.
Vicky
A: 

The period "." is a shell built-in, and executes the script "in-place", analogous to #include in C/C++. Using "." outside of a shell-script has no meaning.

If you want to run the script from Java, you have to execute the script interpreter (/bin/ksh):

  Runtime.getRuntime().exec("/bin/ksh batchstart.sh", ...)

but note that this is not semantically equivalent, since you're executing batchstart.sh as a sub-process instead of sourcing it.

JesperE
A: 

When run from the command line, using a dot at the start of a script indicates that the script should be run in the current environment, instead of spawning a new subshell and using a new copy of the current environment. This allows you to export a new value of an environment variable to be used by commands run later from the same interactive shell.

Obviously, this technique only works if you are running your batchstart.sh script from an actual shell. Java does not know how this mechanism works and so the dot means nothing to it. A script cannot modify the environment of the Java process it was called from.

If your goal is to change the value of the DISPLAY environment variable for other commands run by your Java process, consider using the ProcessBuilder class to specify a new environment for the child process. Java does not contain a built-in way to modify variables in its own environment.

Greg Hewgill