6

I use following Java code to launch a Terminal:

final ProcessBuilder processBuilder = new ProcessBuilder("/usr/bin/open", "-b",
                                                         "com.apple.Terminal",
                                                         "/Volumes");
final Map<String, String> environment = processBuilder.environment();
final String path = environment.get("PATH");
environment.put("PATH", "/mypath" + File.pathSeparator + path);
final Process process = processBuilder.start();
process.waitFor();

This causes a terminal window to be opened, but the modifications of PATH seem to be ignored:

Volumes$ echo $PATH
/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin

This problem seems to be related to how I'm launching the Terminal. Launching other applications with modifying the environment variables works fine.

How to launch the Terminal so that it accepts my environment variable modifications, even if the Terminal already is open?

Thomas S.
  • 5,804
  • 5
  • 37
  • 72
  • I believe it's being ignored because the "environment" would be for the shell that launched Terminal, not the Terminal itself. It's like you opened a Terminal and launched another Terminal from CLI, then changed the PATH of the first Terminal. You would probably need to check out the Terminal.app to see if you can possibly pass in things for it to run, like `export PATH=xxx` – CodeChimp Nov 07 '13 at 12:24
  • Maybe this link can help? http://stackoverflow.com/questions/318239/how-do-i-set-environment-variables-from-java – Jon Nov 07 '13 at 13:28
  • @Jon: No, this does not help. Did you fully read my question? – Thomas S. Nov 07 '13 at 14:59
  • I did read the question. – Jon Nov 07 '13 at 16:18
  • What shell does Yerminal open - it is in its preferences? – mmmmmm Nov 25 '13 at 22:45

2 Answers2

4

In this particular case you could fake it by using AppleScript to open the terminal:

final ProcessBuilder processBuilder = new ProcessBuilder(
      "/usr/bin/osascript", "-e");
final Map<String, String> environment = processBuilder.environment();
final String path = environment.get("PATH");
processBuilder.command().add("tell application \"Terminal\" to do script \"" +
    "cd /Volumes ; export PATH=\\\"/mypath:" + path + "\\\\"\"");
final Process process = processBuilder.start();
process.waitFor();

If you want to populate the cd directory and the PATH value from user-supplied parameters then I would consider using on run to let the script take arguments, and use quoted form of to avoid any potential issues with quoting:

final ProcessBuilder processBuilder = new ProcessBuilder("/usr/bin/osascript",
   "-e", "on run argv",
   "-e", "  tell application \"Terminal\" to do script "
            + "\"cd \" & quoted form of item 1 of argv & \" ; "
            + "export PATH=\" & quoted form of item 2 of argv",
   "-e", "end run");

// create a File and use getAbsolutePath to resolve any relative paths
// against this Java process's working directory.
File cdDir = new File(dirParam);

// this argument will be "item 1 of argv" to the AppleScript
processBuilder.command().add(cdDir.getAbsolutePath());

final Map<String, String> environment = processBuilder.environment();
final String path = environment.get("PATH");

File pathPrefix = new File(pathParam);
// and this will be "item 2 of argv"
processBuilder.command().add(
      pathPrefix.getAbsolutePath() + File.pathSeparator + path);

final Process process = processBuilder.start();
process.waitFor();

If you can't do the getAbsolutePath trick on the Java side but still want relative paths (relative to the current directory of osascript) to work, then you'd need a trick like this:

final ProcessBuilder processBuilder = new ProcessBuilder("/usr/bin/osascript",
   "-e", "on run argv",
   "-e", "  set currentdir to (do shell script \"pwd\")",
   "-e", "  set currentpath to (do shell script \"echo $PATH\")",
   "-e", "  tell application \"Terminal\" to do script \""
            + "cd \" & quoted form of currentdir & \" ; ""
            + "cd \" & quoted form of item 1 of argv & \" ; "
            + "export PATH=\" & quoted form of currentpath",
   "-e", "end run",
   dirParam);

final Map<String, String> environment = processBuilder.environment();
final String path = environment.get("PATH");
environment.put("PATH", pathParam + File.pathSeparator + path);

final Process process = processBuilder.start();
process.waitFor();

This is a bit of a Russian doll - we're executing osascript from Java, which in turn runs its own subshell to do pwd and echo $PATH to get the values of the current working directory and the PATH variable of the osascript process into AppleScript variables, which we then inject into the new Terminal. By first doing a cd to the "current" dir, and then another cd to the specified dirParam this will handle both relative and absolute paths.

Finally note that osascript prints the return value of the script, so you should make sure you consume all the process output before doing waitFor (or if you're on Java 7 you can use processBuilder.redirectOutput(new File("/dev/null")) and the same for redirectError).

Ian Roberts
  • 120,891
  • 16
  • 170
  • 183
  • Thanks. I'm no osascript expert - can this be tweaked, so the paths can be used from the current working dir and the PATH passed as parameter (aka works with ANY specified path)? – Thomas S. Nov 25 '13 at 18:27
  • @ThomasS. I've added some more detail about how you could approach this, using `new File(relativePath).getAbsolutePath()` – Ian Roberts Nov 25 '13 at 22:41
  • Is it possible to get the current working directory from the script itself (our Java code sets the working directory of the launched command and can't be changed to provide it as a separate parameter). – Thomas S. Nov 26 '13 at 11:00
  • @ThomasS. how about this? I couldn't find a native AppleScript way to access the current working directory of the osascript process, but you can use `do shell script "pwd"` to achieve the same thing by spawning a subshell. – Ian Roberts Nov 26 '13 at 11:46
2

A couple of thoughts - I can't try anything out as I only have Linux at work.

You're opening the terminal via the open command. Perhaps if you run the terminal executable directly (I guess you need to go to the actual binary inside the .app folder) your environment settings would pass through?

Alternatively, another way of starting the terminal is to open a saved Terminal preferences file (extension .terminal, created via the Preferences panel of Terminal.app). This opens a Terminal with those preferences set. I haven't tinkered with Terminal preferences for years, but perhaps there's a way to set the path in there?

Pete Verdon
  • 335
  • 1
  • 10