Add fish_add_path, a simple way to add to $PATH
This is a function you can either execute once, interactively, or stick in config.fish, and it will do the right thing. Some options are included to choose some slightly different behavior, like setting $PATH directly instead of $fish_user_paths, or moving already existing components to the front/back instead of ignoring them, or appending new components instead of prepending them. The defaults were chosen because they are the most safe, and especially because they allow it to be idempotent - running it again and again and again won't change anything, it won't even run the actual `set` because it skips that if all components are already in. Fixes #6960.
This commit is contained in:
parent
ba116f1d3b
commit
9354dd6971
66
doc_src/cmds/fish_add_path.rst
Normal file
66
doc_src/cmds/fish_add_path.rst
Normal file
@ -0,0 +1,66 @@
|
||||
.. _cmd-fish_add_path:
|
||||
|
||||
fish_add_path - add to the path
|
||||
==============================================================
|
||||
|
||||
Synopsis
|
||||
--------
|
||||
|
||||
::
|
||||
|
||||
fish_add_path [paths...]
|
||||
fish_add_path (-h | --help)
|
||||
fish_add_path [(-g | --global) | (-U | --universal) | (-P | --path)] [(-m | --move)] [(-a | --append) | (-p | --prepend)] [(-v | --verbose) | (-n | --dry-run)] [paths...]
|
||||
|
||||
|
||||
Description
|
||||
-----------
|
||||
|
||||
``fish_add_path`` is a simple way to add more components to fish's $PATH. It does this by adding the components either to $fish_user_paths or directly to $PATH (if the ``--path`` switch is given).
|
||||
|
||||
It is (by default) safe to use ``fish_add_path`` in config.fish, or it can be used once, interactively, and the paths will stay in future because of :ref:`universal variables <variables-universal>`. This is a "do what I mean" style command, if you need more control, consider modifying the variable yourself.
|
||||
|
||||
Components are normalized by :ref:`realpath <cmd-realpath>`. This means that trailing slashes are ignored and symlinks are resolved, and relative paths are made absolute. If a component already exists, it is not added again and stays in the same place unless the ``--move`` switch is given.
|
||||
|
||||
Components are added in the order they are given, and they are prepended to the path unless ``--append`` is given (if $fish_user_paths is used, that means they are last in $fish_user_paths, which is itself prepended to $PATH, so they still stay ahead of the system paths).
|
||||
|
||||
If no component is new, the variable ($fish_user_paths or $PATH) is not set again or otherwise modified, so variable handlers are not triggered.
|
||||
|
||||
If a component is not an existing directory, ``fish_add_path`` ignores it.
|
||||
|
||||
Options
|
||||
-------
|
||||
|
||||
- ``-a`` or ``--append`` causes the components to be added to the *end* of the variable
|
||||
- ``-p`` or ``--prepend`` causes the components to be added to the *front* of the variable (this is the default)
|
||||
- ``-g`` or ``--global`` means to use a global $fish_user_paths
|
||||
- ``-U`` or ``--universal`` means to use a universal $fish_user_paths - this is the default if it doesn't already exist
|
||||
- ``-P`` or ``--path`` means to use $PATH directly
|
||||
- ``-m`` or ``--move`` means to move already existing components to the place they would be added - by default they would be left in place and not added again
|
||||
- ``-v`` or ``--verbose`` means to print the :ref:`set <cmd-set>` command used
|
||||
- ``-n`` or ``--dry-run`` means to print the ``set`` command that would be used without executing it
|
||||
|
||||
If ``--move`` is used, it may of course lead to the path swapping order, so you should be careful doing that in config.fish.
|
||||
|
||||
|
||||
Example
|
||||
-------
|
||||
|
||||
|
||||
::
|
||||
|
||||
# I just installed mycoolthing and need to add it to the path to use it.
|
||||
fish_add_path /opt/mycoolthing/bin
|
||||
|
||||
# I want my ~/.local/bin to be checked first.
|
||||
fish_add_path -m ~/.local/bin
|
||||
|
||||
# I prefer using a global fish_user_paths
|
||||
fish_add_path -g ~/.local/bin ~/.otherbin /usr/local/sbin
|
||||
|
||||
# I want to append to the entire $PATH because this directory contains fallbacks
|
||||
fish_add_path -aP /opt/fallback/bin
|
||||
|
||||
# I want to add the bin/ directory of my current $PWD (say /home/nemo/)
|
||||
> fish_add_path -v bin/
|
||||
set fish_user_paths /home/nemo/bin /usr/bin /home/nemo/.local/bin
|
85
share/functions/fish_add_path.fish
Normal file
85
share/functions/fish_add_path.fish
Normal file
@ -0,0 +1,85 @@
|
||||
function fish_add_path --description "Add paths to the PATH"
|
||||
# This is meant to be the easy one-stop shop to adding stuff to $PATH.
|
||||
# By default it'll prepend the given paths to a universal $fish_user_paths, excluding the already-included ones.
|
||||
#
|
||||
# That means it can be executed once in an interactive session, or stuffed in config.fish,
|
||||
# and it will do The Right Thing.
|
||||
#
|
||||
# The options:
|
||||
# --prepend or --append to select whether to put the new paths first or last
|
||||
# --global or --universal to decide whether to use a universal or global fish_user_paths
|
||||
# --path to set $PATH instead
|
||||
# --move to move existing entries instead of ignoring them
|
||||
# --verbose to print the set-command used
|
||||
# --dry-run to print the set-command without running it
|
||||
# We do not allow setting $PATH universally.
|
||||
#
|
||||
# It defaults to keeping $fish_user_paths or creating a universal, prepending and ignoring existing entries.
|
||||
argparse -x g,U -x P,U -x a,p g/global U/universal P/path p/prepend a/append h/help m/move v/verbose n/dry-run -- $argv
|
||||
or return
|
||||
|
||||
if set -q _flag_help
|
||||
__fish_print_help fish_add_path
|
||||
return 0
|
||||
end
|
||||
|
||||
set -l scope $_flag_global $_flag_universal
|
||||
if not set -q scope[1]; and not set -q fish_user_paths
|
||||
set scope -U
|
||||
end
|
||||
|
||||
set -l var fish_user_paths
|
||||
set -q _flag_path; and set var PATH
|
||||
set -l mode $_flag_prepend $_flag_append
|
||||
set -q mode[1]; or set mode -p
|
||||
|
||||
# To keep the order of our arguments, go through and save the ones we want to keep.
|
||||
set -l newpaths
|
||||
set -l indexes
|
||||
for path in $argv
|
||||
# Realpath allows us to canonicalize the path, which is needed for deduplication.
|
||||
# We could add a non-canonical version of the given path if no duplicate exists, but tbh that's a recipe for disaster.
|
||||
|
||||
# realpath complains if a parent directory does not exist, so we silence stderr.
|
||||
set -l p (builtin realpath -- $path 2>/dev/null)
|
||||
|
||||
# Ignore non-existing paths
|
||||
test -d "$p"; or continue
|
||||
|
||||
if set -l ind (contains -i -- $p $$var)
|
||||
# In move-mode, we remove it from its current position and add it back.
|
||||
if set -q _flag_move
|
||||
set -a indexes $ind
|
||||
set -a newpaths $p
|
||||
end
|
||||
else
|
||||
# Without move, we only add it if it's not in.
|
||||
set -a newpaths $p
|
||||
end
|
||||
end
|
||||
|
||||
# Ensure the variable is only set once, by constructing a new variable before.
|
||||
# This is to stop any handlers or anything from firing more than once.
|
||||
set -l newvar $$var
|
||||
if set -q _flag_move; and set -q indexes[1]
|
||||
# We remove in one step, so the indexes don't move.
|
||||
set -e newvar[$indexes]
|
||||
end
|
||||
set $mode newvar $newpaths
|
||||
|
||||
# Finally, only set if there is anything *to* set.
|
||||
# This saves us from setting, especially in the common case of someone putting this in config.fish
|
||||
# to ensure a path is in $PATH.
|
||||
if set -q newpaths[1]; or set -q indexes[1]
|
||||
if set -q _flag_verbose; or set -q _flag_n
|
||||
# The escape helps make it unambiguous - so you see whether an argument includes a space or something.
|
||||
echo (string escape -- set $scope $var $newvar)
|
||||
end
|
||||
|
||||
not set -q _flag_n
|
||||
and set $scope $var $newvar
|
||||
return 0
|
||||
else
|
||||
return 1
|
||||
end
|
||||
end
|
56
tests/checks/fish_add_path.fish
Normal file
56
tests/checks/fish_add_path.fish
Normal file
@ -0,0 +1,56 @@
|
||||
# RUN: %fish %s
|
||||
#
|
||||
# This deals with $PATH manipulation. We need to be careful not to step on anything.
|
||||
|
||||
set -l tmpdir (mktemp -d)
|
||||
mkdir $tmpdir/bin
|
||||
mkdir $tmpdir/sbin
|
||||
mkdir $tmpdir/etc
|
||||
ln -s $tmpdir/bin $tmpdir/link
|
||||
|
||||
# We set fish_user_paths to an empty global to have a starting point
|
||||
set -g fish_user_paths
|
||||
fish_add_path -v $tmpdir/bin
|
||||
# CHECK: set fish_user_paths {{.*}}/bin
|
||||
echo $status
|
||||
# CHECK: 0
|
||||
|
||||
# Confirm that it actually ends up in $PATH
|
||||
contains -- (builtin realpath $tmpdir/bin) $PATH
|
||||
and echo Have bin
|
||||
# CHECK: Have bin
|
||||
|
||||
# Not adding duplicates and not triggering variable handlers
|
||||
function checkpath --on-variable PATH --on-variable fish_user_paths; echo CHECKPATH: $argv; end
|
||||
set PATH $PATH
|
||||
# CHECK: CHECKPATH: VARIABLE SET PATH
|
||||
fish_add_path -v $tmpdir/bin
|
||||
# Nothing happened, so the status failed.
|
||||
echo $status
|
||||
# CHECK: 1
|
||||
functions --erase checkpath
|
||||
|
||||
# Not adding a link either
|
||||
fish_add_path -v $tmpdir/link
|
||||
echo $status
|
||||
# CHECK: 1
|
||||
|
||||
fish_add_path -a $tmpdir/sbin
|
||||
# Not printing anything because it's not verbose, the /sbin should be added at the end.
|
||||
string replace -- $tmpdir '' $fish_user_paths | string join ' '
|
||||
# CHECK: /bin /sbin
|
||||
|
||||
fish_add_path -m $tmpdir/sbin
|
||||
string replace -- $tmpdir '' $fish_user_paths | string join ' '
|
||||
# CHECK: /sbin /bin
|
||||
|
||||
set -l oldpath "$PATH"
|
||||
fish_add_path -nP $tmpdir/etc | string replace -- $tmpdir ''
|
||||
# Should print a set command to prepend /etc to $PATH, but not actually do it
|
||||
# CHECK: set PATH /etc{{.*}}
|
||||
|
||||
# Confirm that $PATH didn't change.
|
||||
test "$oldpath" = "$PATH"
|
||||
or echo "PATH CHANGED!!!" >&2
|
||||
|
||||
exit 0
|
Loading…
x
Reference in New Issue
Block a user