39

I want to add ./bin directory (which is relative to current shell directory) to $PATH on fish startup. Note that fish is a shell.

echo $PATH
set PATH ./bin $PATH
echo $PATH

If I place these lines inside ~/.config/fish/config.fish the shell will echo the same collection of paths. Absolute paths are added properly.

If I open the shell and type the same set PATH ./bin $PATH inside some directory containing bin it is added successfully. However when there is no bin inside current directory it shows me an error.

set: Could not add component ./bin to PATH.
set: Value too large to be stored in data type

I'm running fish 1.23.1 on OS X Lion.

suci
  • 178
  • 7
Simon Perepelitsa
  • 20,350
  • 8
  • 55
  • 74

6 Answers6

70

The best way I have found to persistently add a path to your $PATH is

set -U fish_user_paths $fish_user_paths ~/path/name

This prepends to $PATH. And since it's persistent, the path stays in $PATH on shell restarts.

It's more efficient than putting a command in your config.fish to modify your $PATH, because it only runs once compared to running on every shell restart.

The variable fish_user_paths is intended to be set by the user1, as stated by ridiculousfish, the maintainer of fish.


Consider creating a fish function for convenience: 2

# ~/.config/fish/functions/add_to_path.fish
function add_to_path --description 'Persistently prepends paths to your PATH'
  set --universal fish_user_paths $fish_user_paths $argv
end

And use it as:

$ add_to_path foo bar  # Adds foo/ and bar/ to your PATH

Notes

  1. On that page the author gives the example set -U fish_user_paths ~/bin. This overwrites fish_user_paths with a single value of ~/bin. To avoid losing existing paths set in fish_user_paths, be sure to include $fish_user_paths in addition to any new paths being added (as seen in my answer).

  2. My dotfiles contain a slightly more advanced version that skips adding duplicates https://github.com/dideler/dotfiles/blob/master/.config/fish/functions/add_to_user_path.fish

Community
  • 1
  • 1
Dennis
  • 56,821
  • 26
  • 143
  • 139
  • 1
    More details, discussion here: https://github.com/fish-shell/fish-shell/issues/527 – Gringo Suave Feb 27 '14 at 02:55
  • should -x flag be used for PATH? – Yichuan Wang Sep 14 '15 at 22:10
  • 1
    @YichuanWang there should be no need since [that's already done in the `fish_user_path` handler](https://github.com/fish-shell/fish-shell/blob/2f3123e1757f495afc799f0c734283d4513db0f8/share/config.fish#L121) – Dennis Sep 16 '15 at 16:16
16

I'd never heard of fish before this. I just installed it so I could try it out (and deleted a few paragraphs I had written here before realizing that fish is a shell).

It looks like set PATH dir-name $PATH is the right syntax to prepend a directory to $PATH.

But adding a relative directory name to $PATH is almost certainly a bad idea, and your shell is doing you a favor by warning you when the directory doesn't exist. (fish is designed to be user-friendly.)

Use an absolute path instead:

set PATH $PWD/bin $PATH

and first check whether $PWD/bin exists, printing an error message if it doesn't.

As for the "set: Value too large to be stored in data type" message, could you be adding the directory to your $PATH multiple times? There should be some way to check whether a directory is already in $PATH before adding it.

Keith Thompson
  • 254,901
  • 44
  • 429
  • 631
  • But I really want `./bin` be in my PATH, so that if I `cd` in some directory I can call any executables in its `./bin` subdirectory without prepending `bin/`. Zsh allows me to do this. $PWD/bin will be set once and won't be relative to current directory. – Simon Perepelitsa Aug 15 '11 at 16:30
  • 9
    I'd say that's a bad idea for the same reason having `.` in your `$PATH` is a bad idea. Suppose you `cd` into `/tmp/bad`, and somebody has created a `/tmp/bad/bin/ls` that runs `rm -rf $HOME`. If I know there's a command in `./bin` that I want to execute, I can type `bin/whatever`; if I don't, I probably don't want to execute it. But if you insist, it looks like `fish` only checks whether the directory exists when you run the `set` command. Just make sure you're in a directory that has a `./bin` subdirectory when you set your `$PATH`; it will stay there when you `cd` to other directories. – Keith Thompson Aug 15 '11 at 16:57
11

I think the answer is that using set -U is a red herring. Instead, add the following to ~/.config/fish/config.fish:

if status --is-interactive
    set PATH $PATH ~/.local/bin;
end
dhardy
  • 11,175
  • 7
  • 38
  • 46
  • 1
    sorry, this probably doesn't answer the original question, but I'll leave it for anyone else looking for a hint! – dhardy Apr 21 '12 at 16:54
7

direnv http://direnv.net/ is a good utility to help with what you're doing.

Generally, prepending $PATH with ./bin is insecure, as anyone with write-access to a shared directory could hide malicious code in e.g. ./bin/ls. That code would execute when you run ls in the shared directory.

direnv does not solve this problem (it works based on .envrc files, but anyone could be placing those), but at the very least it makes you aware when you cd into a directory that $PATH is getting modified:

$ cd my_project
direnv: loading .envrc
direnv export: ~PATH
Florian D
  • 81
  • 1
  • 1
  • Why the downvote? Direnv seems like a useful tool for the problem, plus the mention of security implications is something that most here didn't think of. – tew Apr 24 '14 at 14:53
  • Newer version of direnv actually address this attack vector by requiring the user to whitelist allowed `.envrc` files. Whenever a new `.envrc` file is encountered direnv won't allow it's execution until the user ran `direnv allow` or (`direnv edit` and saved the file). – zimbatm Dec 02 '14 at 13:58
2

It seems like fish won't add a non-existing directory path to PATH. That applies to relative paths too. But if you create bin directory in your home directory set PATH ./bin $PATH will work properly on each startup since it is executed from home. This is kind of a hack though.

Simon Perepelitsa
  • 20,350
  • 8
  • 55
  • 74
0

Personally, I think there is only a small risk in adding . to the $PATH, so long as it's the last item, because a rogue ls (or whatever) in the CWD will not be found before /usr/bin/ls.

I add this in my config.fish:

contains '.' $PATH; or set --export PATH $PATH .

You could do similar for adding ./bin, again in the last position:

contains './bin'; or set --export PATH $PATH ./bin

Two things are going on here:

  1. This is setting $PATH directly, just as with other shells. This won't persist across shells.
  2. This checks that it's not already in the $PATH, so that sub-shells won't get longer, redundant entries in $PATH
mike
  • 1,854
  • 17
  • 23