0
  • I'm trying to do some automation on MacOS with Java.
  • no problems when running the commands manually from a terminal
  • i assume it works because of <user.home>/.zprofile
  • the commands are not found when trying to execute them via ProcessBuilder

How can I execute commands with the same environment as if running a zsh terminal manually?

import java.io.BufferedReader;
import java.io.InputStreamReader;

public class Test {
  public static void main(String[] args) throws Exception {
    // these commands work
    run("/bin/sh", "-c", "echo $PATH");
    run("/bin/bash", "-c", "echo $PATH");
    run("/bin/zsh", "-c", "echo $PATH");

    // these commands all work when I run them manually in a terminal
    // but fail here with "zsh:1: command not found: ..."
    run("/bin/zsh", "-c", "node -v");
    run("/bin/zsh", "-c", "npm -v");
  }

  private static void run(String... command) throws Exception {
    ProcessBuilder processBuilder = new ProcessBuilder();
    processBuilder.redirectErrorStream(true);
    processBuilder.command(command);
    Process process = processBuilder.start();
    try(BufferedReader br = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
      for(String line = br.readLine(); line != null; line = br.readLine()) {
        System.out.println(line);
      }
    }
    System.out.println("return value: " + process.waitFor());
  }
}

Output:

/usr/bin:/bin:/usr/sbin:/sbin
return value: 0
/usr/bin:/bin:/usr/sbin:/sbin
return value: 0
/usr/bin:/bin:/usr/sbin:/sbin
return value: 0
zsh:1: command not found: node
return value: 127
zsh:1: command not found: npm
return value: 127
Reto Höhener
  • 5,419
  • 4
  • 39
  • 79
  • You can either have a look at this https://stackoverflow.com/a/318252/8462076 or specify full path for your binaries (`which node` for getting `node` full path). But IMO you should really consider the 1st option ;) – peyo May 20 '21 at 16:11

1 Answers1

0

After reading way too many articles about shells, and studying shell init diagrams, I decided to go with Zsh.

The reason was this blog post, which indicated that Zsh seems to have at least one init file that is executed for all possible shell variants (login, non-login, interactive, non-interactive etc).

I moved all my environment setup (PATH and LANG) to /etc/zshenv, deleted /etc/zprofile and all ~/.z* files.

I also changed the shell for both root and my user to Zsh (for the user this can also be done via system preferences):

  dscl . -delete /Users/root UserShell && dscl . -create /Users/root UserShell /bin/zsh && dscl . -read /Users/root UserShell
  dscl . -delete /Users/reto UserShell && dscl . -create /Users/reto UserShell /bin/zsh && dscl . -read /Users/reto UserShell

Now I get the same environment for:

  • SSH as root
  • SSH as user
  • Terminal.app
  • Processes started from Java
  • And pretty much everything else so far

So far so good. Test program output:

/usr/bin:/bin:/usr/sbin:/sbin
return value: 0
/usr/bin:/bin:/usr/sbin:/sbin
return value: 0
/opt/local/bin:/opt/local/sbin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin
return value: 0
v14.17.0
return value: 0
6.14.13
return value: 0
Reto Höhener
  • 5,419
  • 4
  • 39
  • 79