3

In the Terminal app my $PATH is:

/usr/local/opt/python/libexec/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/Library/Apple/usr/bin

If the user starts my C++ application using Dock, its $PATH is:

/usr/bin:/bin:/usr/sbin:/sbin

I would like my app to always has the same $PATH as terminal (bash) has. Is there an easy way to achieve this?

The only way I'm aware of for now is to create a bash script on the disk with something like echo $PATH, launch it from my C++ program using bash -l command, read the output and update my process' PATH variable accordingly.

And, I do not want to change user system's config in any way. This should work without any additional actions required from the user and do not affect the user's system in any way.

Braiam
  • 1
  • 11
  • 47
  • 78
Alexander Dyagilev
  • 1,139
  • 1
  • 15
  • 43
  • Welcome to Ask Different. I apologize that you were told on SO to ask a code level question here. Unless there’s something more generic you’re trying to solve that will likely get migrated back to SO without a substantial edit to the premise of the question. – bmike Nov 02 '21 at 10:24
  • There's nothing macOS-specific here, so scouring StackOverflow is likely best. Otherwise try looking at getenv("PATH"): https://en.cppreference.com/w/cpp/utility/program/getenv – JBRWilkinson Nov 02 '21 at 10:33
  • @bmike But they did close my question on SO... – Alexander Dyagilev Nov 02 '21 at 10:27
  • @GordonDavisson Yeah, my other Unix is showing through. You're right, user accounts are not in there. Bad on my part. – Marc Wilson Nov 02 '21 at 05:20
  • 1
    @MarcWilson On macOS, /etc/passwd is a stub that doesn't contain anything useful. The actual accounts are stored in /var/db/dslocal/nodes/Default/users/ as .plist files. Or possibly in a network domain or something. `dscl` is a good way to get user info. – Gordon Davisson Nov 02 '21 at 00:45
  • 2
    Re your question about `bash` not being a default shell... asking `bash` what *my* $PATH is would be more or less useless. You can't assume that the user's shell is `bash`, or that they've invested any effort in configuring `bash` if it is not. – Marc Wilson Nov 01 '21 at 22:58
  • @MarcWilson Thanks! – Alexander Dyagilev Nov 01 '21 at 23:00
  • What will happen if `bash` is not a default shell, when I call it? I suspect it can still return me the proper `$PATH`, isn't? – Alexander Dyagilev Nov 01 '21 at 22:52
  • OK, I can get the default shell using `dscl . -read /Users/username UserShell`. But how do I know the required command line switch for it (which is `-l` for bash)? – Alexander Dyagilev Nov 01 '21 at 22:49
  • 2
    I have to say, you don't want to try to interpret the startup files yourself. That's what the shell is for. ^_^ Just start a non-interactive login shell and read its environment. I know BBEdit does this because v14 was not setting the path correctly in Unix Worksheets, and I asked their tech support how they determined the path, then got them to add `ksh` to their list of shells. Quote: "BBEdit will never set $PATH; when it starts up, it'll start a non-interactive login shell and run a `printenv` command, and interpret the output to find `PATH`." – Marc Wilson Nov 01 '21 at 22:48
  • You would have to have your app work out what shell the user uses and then call it or interpret it's startup files. – mmmmmm Nov 01 '21 at 22:17
  • That's certainly a reasonable way to do it. BBEdit, from Bare Bones Software, does exactly that. I caution you to not assume that the user's preferred shell is actually bash, but that you read their default shell and use that. – Marc Wilson Nov 01 '21 at 21:55
  • You can also get the user's shell just by reading /etc/passwd. As for the argument, you are really looking at shells that are sh-compatible (for which you just pass '-l', and ones that are not. For the ones that are not, `tcsh` and `fish`, at least, also use '-l', although they may have a different way to read the environment afterwards. – Marc Wilson Nov 01 '21 at 22:55
  • @JBRWilkinson The problem exists under `macOS` only! And only in case the user launches an app from the `Dock`! – Alexander Dyagilev Nov 02 '21 at 11:03
  • Why do your app cares about the PATH value? – Braiam Nov 02 '21 at 15:18

2 Answers2

3

If you don’t like your solution of calling bash, here’s a stub to exercise more control over invoking shells and perhaps test if the user default shell isn’t bash all from within a c++ program:

setenv("PATH", "/MyCustomPath:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin", 1);

To Read bash's path:

std::string exec(const char* cmd) {
    char buffer[128];
    std::string result = "";
    FILE* pipe = popen(cmd, "r");
    if (!pipe) throw std::runtime_error("popen() failed!");
    try {
        while (fgets(buffer, sizeof buffer, pipe) != NULL) {
            result += buffer;
        }
    } catch (...) {
        pclose(pipe);
        throw;
    }
    pclose(pipe);
    return result;
}

setenv("PATH", exec("bash -l -c 'echo -n $PATH'").c_str(), 1);
bmike
  • 917
  • 2
  • 22
  • 41
Gintaras
  • 214
  • 2
  • 8
  • Are you sure the question is about how to set the PATH environment variable? It's useful to first read and understand a question before answering it. :) – Alexander Dyagilev Nov 02 '21 at 09:31
  • every program chooses it's own environment variables or inherits from the parent process. To ensure that your program always have the same path as bash, the program itself must call SETENV. the program can read and set bash's profile if that's intended. – Gintaras Nov 02 '21 at 09:34
  • That fails any c++ code review (That code is apart from the characters std:: plain C code not using any of C++ improvements. – mmmmmm Nov 02 '21 at 10:02
  • @mmmmmm Maybe code is not good (I do not care, I'm writing my own, not just copy/paste), but now it contains some very useful information for me. E.g. I did not know how to run shell script without a script file on disk. :) Yes, I am very newbie in Unix :) – Alexander Dyagilev Nov 02 '21 at 10:07
  • The code is not great i agree, (c derivative and limited buffer), i just gave an idea how it should work. The author of the question or someone else may improve upon the code – Gintaras Nov 02 '21 at 10:09
  • @Gintaras I think, you should launch bash with `-l` argument and so do not use `source` at all. Just `echo` the PATH variable. Am I wrong? I think no.. Fix your code (launch with `-l`) if you want to, and I'll mark it as an answer then. Thanks anyway :) – Alexander Dyagilev Nov 02 '21 at 10:16
  • My way (using `-l`) is more stable (I think), but its output can also contain some garbage from user-defined scripts. So, I think, it's useful to put some unique mark before echo PATH and use regex then. – Alexander Dyagilev Nov 02 '21 at 10:19
  • 2
    Bash behaves differently when invoked interactively. Maybe add an `-i` flag to force it to be interactive if that's what you want. – tripleee Nov 02 '21 at 10:49
  • That's why you invoke the shell, and run `/usr/bin/printenv` in it. You are not going to be able to deal with random whatever that shell rc files might output, so ignore it, and capture the output of the command. – Marc Wilson Nov 02 '21 at 14:43
0

Launching an application from Shell/Explorer/Dock/etc gets a different environment to the Command Line/Terminal/bash in many scenarios because the Shell/Explorer/Dock was invoked by the GUI login process and not the Command Line/Terminal/Bash.

When you run Terminal/Bash, the .bashrc/.bash_profile, etc scripts are run, and these are often configured to add things to the PATH environment variable for command-line convenience.

It's not clear from the question whether your application will be used by other users or just yourself:

  1. Can you change the 'Dock Icon' to point to a Shell script that sets up the PATH environment variable before launching the C++ application?

  2. Failing that, if it's only for you, you can hack your systemwide bashrc: sudo $EDITOR /etc/bashrc ..but this is not recommended

  3. If your C++ application has a dependency on something in the $PATH, can you code around it? For example, use the absolute path to whatever application you need to run.

JBRWilkinson
  • 4,821
  • 1
  • 24
  • 36