prezto: The notice about `path_helper` on OS X is incorrect

This:

If you have administrator privileges, you must fix an Apple-introduced problem in Mac OS X 10.5 Leopard by executing the following command, or BASH and Zsh will have the wrong PATH when executed non-interactively.

sudo chmod ugo-x /usr/libexec/path_helper

Is incorrect. Users shouldn’t disable path_helper.

The problem is OS X is that it comes with /etc/zshenv that uses path_helper. However, /etc/zshenv gets sourced for every zsh shell, even non-interactive. To put a stop to that, simply:

sudo mv /etc/{zshenv,zprofile}

About this issue

  • Original URL
  • State: closed
  • Created 11 years ago
  • Reactions: 1
  • Comments: 31 (14 by maintainers)

Commits related to this issue

Most upvoted comments

@mislav, I understand how it works. It’s broken.

I also understand that having it enabled causes problems. You forget the path set in ~/.MacOSX/environment.plist, which is needed for GUI apps, and Terminal.app, being a GUI app, will get that environment before it launches a shell subprocesses.

path_helper reorders the PATH entries set in environment.plist, which causes GUI programs to fail when they execute their scripts via a shell subprocess. A good example is any editor that depends on Exuberant Ctags. path_helper reorders the PATH entries and /usr/bin/ctags gets executed instead of Exuberant Ctags that is installed elsewhere. The editor complains that the wrong ctags is installed.

If I don’t set the PATH in environment.plist for GUI apps, then they complain that they can’t find programs.

Setting PATH on Mac OS X is a mess, and path_helper doesn’t help.

It’s too late to modify PATH in ~/.zshrc for essential path entries that must exist for non-interactive shells. Hence why ~/.zshenv exists. There is a README in the runcoms directory that explains all the Zsh files.

Except for the Ruby, Python, and Perl paths that are added by their respective Prezto modules, the rest of the home directory path entries are set in ~/.zshenv, which is a copy of the runcoms/zshenv file.

# Set the list of directories that Zsh searches for programs.
path=(
  $HOME/.tilde/{bin,sbin}
  $HOME/.homebrew/{bin,sbin}
  /usr/local/{bin,sbin}
  /usr/{bin,sbin}
  /{bin,sbin}
  $path
)

path_helper is not needed. Apple could have implemented /etc/paths.d properly by using shell code, just like I have, but it looks like they spent 10 minutes to write a broken C program instead.

path_helper is broken. Dancing around the issue causes nothing but frustration. The only real solution, without editing /etc shell init files, is to replace the Apple supplied path_helper with a fixed one that doesn’t rearrange path entries. It’s a simple C program.

Anyway, for anybody who’ll land here too, here is a workaround:

in .zshenv: before customizing PATH:

# MacOS workaround sneaky path_helper in /etc/zprofile
if [ -x /usr/libexec/path_helper ]; then
  eval `/usr/libexec/path_helper -s`
fi

At the end of .zshenv:

# MacOS workaround sneaky path_helper in /etc/zprofile
export GOODPATH=$PATH

Create a .zprofile (I don’t usually have one):

# MacOS workaround sneaky path_helper in /etc/zprofile
export PATH=$GOODPATH

Now everything works as intended.

@pdemarti, if we consider unix shell initialization, then we may come to conclusion that /etc/zprofile is not called in some cases. At least in the case when we

Executing a command remotely with ssh

In addition, it slows down the terminal session loading process.

So, I suppose the correct solution would be NOT to include

if [ -x /usr/libexec/path_helper ]; then
  eval `/usr/libexec/path_helper -s`
fi

into ~/.zshenv, but to save PATH at the END of ~/.zshenv

export PATH_SAVE=$PATH

as you, indeed, did.

Then, you just need to set PATH in ~/.zprofile, such that it would be

export PATH=$PATH_SAVE:$PATH

In this case you just prepend your path from resulting PATH from ~/.zshenv to the PATH that was mutated by /etc/zprofile.

In order to remove duplications and not just merge two paths you can use instead of this

export PATH=$PATH_SAVE:$PATH

this script:

# add unique paths from PATH to PATH_SAVE and replace PATH with PATH_SAVE
if [ -n "$PATH_SAVE" ]; then
  PATH_SAVE=:$PATH_SAVE:;
  PATH=$PATH:
  while [ -n "$PATH" ]; do
   x=${PATH%%:*}  # the first remaining entry
   case $PATH_SAVE in
     *:$x:*) ;;  # already there
     *) PATH_SAVE=$PATH_SAVE$x: ;;  # not there yet
   esac
   PATH=${PATH#*:}
  done
  PATH=${PATH_SAVE:1:-1}
  unset PATH_SAVE x
  export PATH
fi

Reordering entries in /etc/paths realy feels like a less intrusive way of fixing this.

if you google path_helper, you’ll notice that it has caused nothing but frustration ever since it was introduced in Leopard

I’ve searched for results in the past year and it doesn’t seem like people have real issues with it. Support questions on forums/mailing lists are from people that struggle to find out how to edit their PATH and what’s the right configuration file for that. The Hackernews comment about path_helper says that it used to be slow, but that’s not the case since the C rewrite. As for the supposed bugs it has, I haven’t found any real report of someone finding a bug with it.

Here’s what path_helper does, and is designed to do: It takes the current PATH and adds global entries to it, moving them to front if they already exist in PATH. Yes, it reorders entries in the PATH, because it was designed to run as the first thing in a login shell, not as the last. You’re just using it wrong, and then claim frustration with the tool.

I don’t think it’s evil to recommend people disable path_helper

@telemachus: People blindly follow instructions, especially when copy/pasting from README files, and forget that they do so. Especially when they don’t know what path_helper is (and most don’t). A Prezto user might switch back to bash and wonder why their system doesn’t work like before. They won’t remember to chmod +x path_helper

The bottom line (to me) is that OSX defaults to a non-standard $PATH and many users will want to adjust that. There are different ways to fix the problem.

Agreed. And I am advocating the least invasive way to fix the problem here: let path_helper do its thing and then customize it further to your needs.

Editors like Vim or Emacs start these shells when executing shell commands (i.e. M-x compile or M-x grep in Emacs). Git, too, starts such a shell to start an Editor. With path_helper enabled,git commitwill always start OS X builtin/usr/bin/vim/even if there is a custom installation in/usr/local/bin` (e.g. via Homebrew).

Yes. path_helper was only flawed in zsh, due to misplaced /etc/zshenv. Bash sources /etc/profile (which uses path_helper) only for login shells, not for other interactive or non-interactive shells.

There’s nothing wrong with path_helper per se.