How to Access the Terminal's $Path Variable from Within My MAC App, It Seems to Uses a Different $Path

How to access the Terminal's $PATH variable from within my mac app, it seems to uses a different $PATH

Adding bash Shell Path

The default shell paths can be found in /etc/paths and /etc/path.d/. One way to read the shell paths is to use the path_helper command. Extending the code example above and using bash as the shell:

let taskShell = Process()
var envShell = ProcessInfo.processInfo.environment
taskShell.launchPath = "/usr/bin/env"
taskShell.arguments = ["/bin/bash","-c","eval $(/usr/libexec/path_helper -s) ; echo $PATH"]
let pipeShell = Pipe()
taskShell.standardOutput = pipeShell
taskShell.standardError = pipeShell
taskShell.launch()
taskShell.waitUntilExit()
let dataShell = pipeShell.fileHandleForReading.readDataToEndOfFile()
var outputShell: String = NSString(data: dataShell, encoding: String.Encoding.utf8.rawValue) as! String
outputShell = outputShell.replacingOccurrences(of: "\n", with: "", options: .literal, range: nil)
print(outputShell)

let task = Process()
var env = ProcessInfo.processInfo.environment
var path = env["PATH"]! as String
path = outputShell + ":" + path
env["PATH"] = path
task.environment = env
task.launchPath = "/usr/bin/env"
task.arguments = ["/bin/bash", "-c", "echo $PATH"]
let pipe = Pipe()
task.standardOutput = pipe
task.standardError = pipe
task.launch()
task.waitUntilExit()
let data = pipe.fileHandleForReading.readDataToEndOfFile()
var output: String = NSString(data: data, encoding: String.Encoding.utf8.rawValue) as! String
output = output.replacingOccurrences(of: "\n", with: "", options: .literal, range: nil)
print(output)

Note:

  1. This code example calls the shell in non-interactive mode. This means that it won't execute any user specific profiles; such as /Users/*userid*/.bash_profile.
  2. The paths can be listed multiple times in the PATH environment variable. They will be traversed from left to right.

References

There are a couple of threads on application and shell PATH's for OS X which provide more context
How to set system wide environment variables on OS X Mavericks
and
Setting the system wide path environment variable in mavericks

How can I see the current value of my $PATH variable on OS X?

You need to use the command echo $PATH to display the PATH variable or you can just execute set or env to display all of your environment variables.

By typing $PATH you tried to run your PATH variable contents as a command name.

Bash displayed the contents of your path any way. Based on your output the following directories will be searched in the following order:

/usr/local/share/npm/bin
/Library/Frameworks/Python.framework/Versions/2.7/bin
/usr/local/bin
/usr/local/sbin
~/bin
/Library/Frameworks/Python.framework/Versions/Current/bin
/usr/bin
/bin
/usr/sbin
/sbin
/usr/local/bin
/opt/X11/bin
/usr/local/git/bin

To me this list appears to be complete.

Setting PATH environment variable in OSX permanently

You have to add it to /etc/paths.

Reference (which works for me) : Here

Swift mac app, run terminal command without knowing the path (so it looks in every path in $PATH)?

You can execute the command via env:

env utility argument ...

Example:

let path = "/usr/bin/env"
let arguments = ["ls", "-l", "/"]
let task = Process.launchedProcess(launchPath: path, arguments: arguments)
task.waitUntilExit()

env locates the given utility using the $PATH variable and
then executes it with the given arguments. It has additional
options to specify a different search path and additional
environment variables.

(This is not a feature of Swift but of macOS and many other operating systems.)

When started from the Finder (double-click) the PATH may be different
from the PATH in your shell environment. If necessary, you can add
additional directories:

var env = task.environment ?? [:]
if let path = env["PATH"] {
env["PATH"] = "/usr/local/bin:" + path
} else {
env["PATH"] = "/usr/local/bin"
}
task.environment = env

execlp and friends also locate the executable using $PATH but offer only the "raw" C interface.

How to launch a terminal app on PATH in Swift on macOS?

A comment from @MartinR brought me to the right idea. Don't run the terminal command directly, but from a new shell. This way it will do the PATH resolution for you:

    let shellProcess = new Process();
shellProcess.launchPath = "/bin/bash";
shellProcess.arguments = [
"-l",
"-c",
// Important: this must all be one parameter to make it work.
"mysqlsh --py -e 'print(\"Call from shell\")",
];
shellProcess.launch();

This example uses the MySQL shell as example, which is in /usr/local/bin (unlike git, which is in /usr/bin). Git worked already in the beginning, while mysqlsh did not. From the comment you can also see that it is important to make the mysqlsh call a complete and single parameter entry. If you split that then /bin/bash -c will only execute mysqlsh and not pass on the given shell parameters.

Where is the default terminal $PATH located on Mac?

If you do sudo man path_helper, it talks a bit about how it puts the path together. You might look in /etc/paths and /etc/paths.d. I did, and found what I was looking for.

Set global $PATH environment variable in VS Code

If you only need the $PATH to be set in the integrated terminal, you can use VS Code's terminal.integrated.env.<platform> variable (added in version 1.15). Press Cmd+Shift+P (or Ctrl+Shift+P) and search for "Preferences: Open Settings (JSON)". Then add the following entry to the settings file:

"terminal.integrated.env.osx": {
"PATH": "...:/usr/bin:/bin:..."
}

(Replace .osx with .linux or .windows as needed.)

To see your system's $PATH, type echo "$PATH" in Terminal.app, and copy and paste it into the settings snippet above.


As for having the $PATH available everwhere in VS Code, so that it will
be used by extensions that call binaries, the only workaround I've found so far is this:

  1. Configure your shell (bash by default) to have the $PATH you want. For example, my ~/.bash_profile has the following line:

    PATH="$PATH:$HOME/bin"
  2. In VS Code, press ⇧⌘P and type install 'code' command if you haven't done so before.

  3. Quit VS Code.

  4. Launch VS Code not by clicking the icon in the dock or in Launchpad, but by opening Terminal.app and typing code. Your newly set path will be active in VS Code until you quit it.

  5. If VS Code restarts, for example due to an upgrade, the $PATH will reset to the system default. In that case, quit VS Code and re-launch it by typing code.

Modify PATH when launching Terminal from Java

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).



Related Topics



Leave a reply



Submit