How to add a directory to your PATH
jvns.ca
jvns.ca
This is easily one of the most annoying ones, especially when shells will have several different locations to read config from depending on how you start the shell, or what a desktop app will do to try and pull the config in (`exec-path-from-shell` in emacs, for example).
And you can't really, say, put it in both your `.zprofile` and `.zshrc` or `.bash_profile`, `.profile`, and `.bashrc`, because then it'll get executed more than once and you'd need to maintain some kind of state to prevent that.
[0]https://jvns.ca/blog/2025/02/05/some-terminal-frustrations/
* If you are using Xorg, you put environment variables in ~/.xprofile
* If you are using Xorg on Debian, you put the environment variables in ~/.xsessionrc
* If you are using Wayland, you make a file in ~/.config/environment.d/ and set you configuration variables there.
There is a similar environment variable for Steam on Linux and I would argue that it should be an application setting but for whatever reason it isn't.
Both of these are hacks around how Xorg (doesn't) handle fractional scaling.
I understand that, but your window server is just another application. On Windows I wouldn't consider scale factor being an env var, and indeed it isn't set as such -- rather that value is set in the registry.
Maybe that's the answer -- Windows has a defacto method of setting system-wide env vars that every application inherits within a given personality but Linux/BSD does not.
I can live with that answer :-)
Not the window server.
QT is a library that applications can use to render their GUIs, so there are many instances of QT in all of the apps that use it.
This is not in any way Wayland specific, and arguably should be the default choice for setting env vars, as it should apply to all user sessions regardless of what type they are or what shell is used.
FWIW, profiles are loaded on 'from scratch' logins; RC files are loaded all the time.
* https://superuser.com/questions/657848/why-do-we-have-login-...
* https://askubuntu.com/questions/879364/differentiate-interac...
Not necessarily. Bash won't do it on its own for a login shell, but there's probably a bit in the /etc/profile your distro provides that does it.
At this point I've pretty much given in and decided that a containerized dev environment is probably the better solution, but on principle it feels so unsatisfying to have to resort to this :(
(I know someone is going to mention Nix/Guix ;) but that feels like a giant rabbit hole)
path_add() {
export PATH=$PATH:$(string_join ':' $@)
}
path_prepend() {
PATH=$(string_join ':' "$@"):$PATH
export PATH
}
These can join an arbitrary list of paths to PATH. e.g. path_add /usr/bin /usr/local/bin ~/bin
They depend on another one: string_join() {
local join=$1; shift
local result=$1; shift
for p in "$@"; do
result="${result}${join}${p}"
done
echo -n "$result"
set +x
}
I also have ones for adding and prepending to LD_LIBRARY_PATH ###################################################################
# Add directory to path
pathadd() {
newelement=${1%/}
if [ -d "$1" ] && ! echo $PATH | grep -E -q "(^|:)$newelement($|:)" ; then
if [ "$2" = "after" ] ; then
PATH="$PATH:$newelement"
else
PATH="$newelement:$PATH"
fi
fi
}
###################################################################
# Remove directory from path
pathrm() {
PATH="$(echo $PATH | sed -e "s;\(^\|:\)${1%/}\(:\|\$\);\1\2;g" -e \
's;^:\|:$;;g' -e 's;::;:;g')"
}Your function does that so +1. Though I'd use
[[ $PATH =~ "(^|:)$newelement($|:)" ]]
over grep -q but it functions the same."export PATH=$DIR:$PATH - That particular pattern is way too common, and is very dangerous if you consider the case when [$DIR or] $PATH (or whatever your variable is, like $LD_LIBRARY_PATH) isn’t set. Then, the value will be :/path/to/dir, which usually means both /path/to/dir and the current directory, which is usually both unexpected behaviour and a security concern."
In Bash:
path_append() { local p; for p; do [[ :"$PATH": =~ :"$p": ]] || PATH+=:$p; done; }
path_prepend() { local p; for p; do [[ :"$PATH": =~ :"$p": ]] || PATH=$p:$PATH; done; }
In portable POSIX shell: path_append() { for p; do case :"$PATH": in *:"$p":*) ;; *) export PATH="$PATH:$p" ;; esac; done; }
path_prepend() { for p; do case :"$PATH": in *:"$p":*) ;; *) export PATH="$p:$PATH" ;; esac; done; } printf '%s\n' "$PATH" | grep --color=auto -E -e '(^|:)'"$( printf '%s\n' "$1" | sed 's/[][\.|$(){}?+*^]/\\&/g' )"'($|:)' > /dev/null 2>&1
if [ "$?" = 1 ]; then PATH="$1:$PATH" ; export PATH ; fi
in my version of pathadd().I attempted this in bash like so:
``` PROMPT_COMMAND="history -a; $PROMPT_COMMAND" ```
But, that meant every shell was sharing the same history. Pressing up would go to the last command run anywhere, not just my current shell.
Sadly, I wasn't even trying to solve the problem Julia is talking about here. I just wanted to make sure my history was saved when I shutdown. Still haven't found a great solution to that. My attempts as using traps and signals caused weird issues.
Want to add to path? `echo ':<directory>' >> /var/share/env/PATH` (or `env --add PATH :<directory>`, or something like that).
function my_env() {
ENVDIR=${ENVDIR:-/var/share/env}
arg="$1"
case "$arg" in
--add)
echo "$3" >> $ENVDIR/"$2"
;;
*)
(
for f in $ENVDIR/*; do
export $(basename $f)="$(< $f | sed -z 's/\n:/:/g')"
done
[[ -z "$arg" ]] && env || env "$arg"
)
;;
esac
}
(Of course, that's a bad approach but your idea is certainly doable in a good way.)I'm not sure I'd stick a path setting in .bashrc - just because it would make it very difficult to ever override that PATH setting elsewhere.
You might want that but it might also mess up some other program's attempt to set the PATH and then e.g. run a shell command.
I usually use .bash_profile or .profile more for this sort of thing and then I have to tell my terminal program to run bash as a login shell and that gives me what I mostly expect.
False things people believe.
https://stackoverflow.com/a/820533
Probably because it's sourced from your distro provided /etc/profile. Bash will only source .bashrc on its own in some cases.
(use-package exec-path-from-shell
:demand t
:config
(exec-path-from-shell-initialize)
(exec-path-from-shell-copy-env "PATH"))Make it as easy as it is in Windows.
On macOS it is arguably harder as you need to use Cmd-Shift-. to reveal dotfiles should you be uncomfortable editing them with vim/nano/etc. When using Omz with Terminal, a path entry could be in any number of files supporting Omz along with my zshrc. Windows doesn't have this issue.
Windows has it's issues, but env vars are significantly easier to manipulate without much knowledge.
setx path "%path%;C:\own\path\"
Sadly this has no error checking so it may mess things up. But it's an option.>All of these directions only work if you’re running the program from your shell. If you’re running the program from an IDE, from a GUI, in a cron job, or some other way, you’ll need to add the directory to your PATH in a different way, and the exact details might depend on the situation.
>I’m honestly not sure how to handle it in an IDE/GUI because I haven’t run into that in a long time, will add directions here if someone points me in the right direction.
The ~/.pam_environment was the equivalent to Windows' environment variables but has been deprecated and supposedly systemd's environment.d took over its functionality.
You can also override the actual PATH variable for an application in the .desktop file as well, or any environment variable, with the Environment key, if that's what she means.
0 https://specifications.freedesktop.org/desktop-entry-spec/la...
On Windows, Microsoft has it easy. The window server is part of the executive; Win32/conhost always exist, even if you choose to run another personality (OS/2, POSIX, et. al.).
I would bet that there is a form of graphical editor somewhere out there on the Internet that someone developed for fun for a given shell.
That must have been what Microsoft was figuring back in the 20th century.
Even in the latest W11, it's the same basic everyday GUI as in Windows XP :)
Just a little more intuitive now.
In XP from the Control Panel click System > Advanced > Environment Variables.
In Win11 from Settings click System > About > Advanced System Settings > Environment Variables.
Pay attention to your choice of User PATH, in addition to System PATH.
Make sure that things like %HOMEDRIVE% that you edit in, actually appear as "C:" once you are done editing, or you're not doing it ideally.
On Windows you just go Win->start typing "environment variable" and you get "Edit the system environment variables - Control Panel". Kinda stupid that you then need to press "Environment Variables..." but I probably wouldn't have to google it.
At least systemd brought some sanity with environment.d, but as this thread (and jvns article) shows it's not very well known. And of course there is still all sorts of weird inherited complexity like pam_env.
I'm confused on the Linux point, because I thought I was the only one who had problems. Until I saw this article! Describes it well. I take responsibility; I am bad at setting env vars in Linux.
So it will never happen.
* the file system doesn't count
User: HKCU\Environment
systemd-kvdb
> set PATH $PATH ~/.npm-global/bin
Fish has some nice utilities for these type of set calls
set --append PATH ~/.npm-global/bin