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:
- 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
. - 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:
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"
In VS Code, press ⇧⌘P and type
install 'code' command
if you haven't done so before.Quit VS Code.
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.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 typingcode
.
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
Conflicting Definition of Swift Struct and Array
Convert Swift Dictionary to String
Variable Captured by Closure Before Being Initialized
Scrollbar Incorrectly Appears Underneath Uicollectionview Section Header
Synchronize Properties in Swift 3 Using Gcd
Uiimage Created from Mtkview Results in Color/Opacity Differences
Do Local Notifications Need User Permission on iOS
How to Pass Protocol with Associated Type (Generic Protocol) as Parameter in Swift
How to Convert Nsset to [String] Array
Scenekit -- How to Get Animations for a .Dae Model
How to Update Swift Dependencies in Xcode
Accessing Self from Instance Properties Which Are Closures
Performseguewithidentifier in Swift
Swift Struct Doesn't Conform to Protocol Equatable
Stop Objects from Colliding Using Spritekit
Add Text Label and Button to Dynamic Tableview Cell Programmatically with Swift
Swift3:How to Handle Precedencegroup Now Operator Should Be Declare with a Body