Replace $PATH with string/file? Set $PATH to null and append? - bash

Backup, first.
Before attempting to resolve this, people are worried about $PATH changes breaking things. Backup your path with echo $PATH > $HOME/bak/conf/path.bak and remember (don't put any raw files into the bak dir). Confirm your backup with cat $HOME/bak/conf/path.bak.
A minimal $PATH!
My desired path is /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin.
This is a minimalist path that should break nothing on most default Linux installs (Backup, first).
I have a huge $PATH that is completely useless (like 120 lines of $PATH--caused by WSL [more later]). So far, I only know how to add to path and remove single directories from $PATH via seg.
Sounds like it should pop right up on search engines. I have a headache in my eye--:
how do I set $PATH to a raw string or purge it so I can append from null?.
Bonus points for anyone who can tell me how to set $PATH from the contents of a file!?
Best guess:
import sys, sys.path.insert(0, '/your/path')--!NO This did not work!
Temporary workaround:
You can move the path to a neutral file (or use whatever means you know) and replace / with / and then use sed to cut the unwanted part out:
PATH=$(echo "$PATH" | sed -e 's/:\/tons:\/of:\/unwanted:\/garbage$//')
Make sure you put everything you don't want between s/ and $//').
Where is this long $PATH coming from?
Related question: How to remove the Win10's PATH from WSL --$PATH is inherited every time you re-launch or perform some other actions such as possibly changing users or environments.
Why is this question different:
As if there isn't enough typing, S.O. has a nuisance dialog that asks me to write and explain why purging $PATH is different from removing one directory/path from $PATH.
Because a punch in the face isn't a kick in the butt.

If I'm understanding your question, you simply want to know how to set your Bash PATH to a fixed string (or erase it completely) and have it persist across restarts of the shell?
If so, then I'll start off by saying that this isn't recommended as it will break other things. If not now, then later.
But if you really want to set the PATH to a fixed value every time Bash starts ...
Short answer first:
Assuming fairly "default" startup files (where ~/.profile sources ~/.bashrc):
Edit your ~/.profile (or ~/.bash_profile in the rare case it exists) and add:
# Remove entirely
unset PATH
export -n PATH
# Optionally
export PATH=/new/pathItem1:/new/path/Item2
Adding this to the bottom of the file should override any other PATH modifications done previously. However, future application installations can make modifications below that, therefore overriding it.
In a very rare case where you have an interactive Bash session that isn't startup by a login shell, you could also make the same modifications to ~/.bashrc.
(Not necessarily required if the modification is the last thing called during startup, but) make sure that there are no other PATH adjustments in your ~/.bashrc or ~/.bash_profile.
More explanation:
Where is this long $PATH coming from?
Your path in Bash in WSL can potentially come from a few sources:
WSL's init process sets a default PATH for the starting application (shell). This is currently (in WSL 0.70.8):
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/usr/lib/wsl/lib
Since this is done before your ~/.profile runs, there's no need to worry about disabling it.
As you've pointed out, WSL automatically appends the Windows path to the Linux path so that you can launch Windows executables. This can be disabled using the method in the question you linked, but it's not necessary if you simply want to override the entire PATH. Setting the PATH in your ~/.profile comes after WSL sets it for the initial process (your shell).
Bash itself has a built-in, default path that is hardcoded into it for "fallback" in case no other path is passed in. This rarely ever takes effect. Most Linux systems are going to set the PATH through some other mechanism, meaning the fallback never gets activated. A typical Linux system will set the default PATH via /etc/environment, but this isn't used under WSL since the init mechanism mentioned above is needed instead.
If using Systemd or another init system in WSL, then Bash (and other POSIX shells) will process /etc/profile and files in /etc/profile.d when starting. These files may contain PATH modifications. Again, since these come before your ~/.profile, (re)setting the PATH to a fixed value will override any of these settings.
Finally, your ~/.profile or ~/.bash_profile (for login shells) and ~/.bashrc (for interactive shells are read. Typically, PATH modifications in these files look something like:
export PATH="/new/path/item:$PATH"
This prepends to the path, making that new item take priority over the previous items.
However, leaving out the existing :$PATH part will simply set it to a string literal.
It should be obvious, but if you do this without including the "normal" OS paths, you will be unable to call any command that isn't built in to Bash already with specifying its fully qualified path. For instance, ls will fail. Note that you can still:
$ ls
ls: command not found
$ /usr/bin/ls
# works
Also note that ~/.profile is called for login shells. It is possible, however, to tell WSL to launch Bash without reading ~/.profile:
wsl ~ -e bash --noprofile
The resulting shell will still have the default WSL path in this case.

Related

zsh: command not found: symfony on ubuntu 20 [duplicate]

I'm using zsh terminal, and I'm trying to add a new entry (/home/david/pear/bin) to the PATH variable. I don't see a reference to the PATH variable in my ~/.zshrc file, but doing echo $PATH returns:
/usr/lib/lightdm/lightdm:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games
So I know that the path variable is being set somewhere. Where is the PATH variable set / modified for the zsh terminal?
Actually, using ZSH allows you to use special mapping of environment variables. So you can simply do:
# append
path+=('/home/david/pear/bin')
# or prepend
path=('/home/david/pear/bin' $path)
# export to sub-processes (make it inherited by child processes)
export PATH
For me that's a very neat feature which can be propagated to other variables.
Example:
typeset -T LD_LIBRARY_PATH ld_library_path :
Here, add this line to .zshrc:
export PATH=/home/david/pear/bin:$PATH
EDIT: This does work, but ony's answer above is better, as it takes advantage of the structured interface ZSH provides for variables like $PATH. This approach is standard for bash, but as far as I know, there is no reason to use it when ZSH provides better alternatives.
You can append to your PATH in a minimal fashion. No need for
parentheses unless you're appending more than one element. It also
usually doesn't need quotes. So the simple, short way to append is:
path+=/some/new/bin/dir
This lower-case syntax is using path as an array, yet also
affects its upper-case partner equivalent, PATH (to which it is
"bound" via typeset).
(Notice that no : is needed/wanted as a separator.)
Common interactive usage
Then the common pattern for testing a new script/executable becomes:
path+=$PWD/.
# or
path+=$PWD/bin
Common config usage
You can sprinkle path settings around your .zshrc (as above) and it will naturally lead to the earlier listed settings taking precedence (though you may occasionally still want to use the "prepend" form path=(/some/new/bin/dir $path)).
Related tidbits
Treating path this way (as an array) also means: no need to do a
rehash to get the newly pathed commands to be found.
Also take a look at vared path as a dynamic way to edit path
(and other things).
You may only be interested in path for this question, but since
we're talking about exports and arrays, note that
arrays generally cannot be exported.
You can even prevent PATH from taking on duplicate entries
(refer to
this
and this):
typeset -U path
PATH pre-populated
The reason your path already has some entries in it is due to your system shell files setting path for you. This is covered in a couple other posts:
Why and where the $PATH env variable is set?
Where is the source of $PATH? I cannot find it in .zshrc
one liner, without opening ~/.zshrc file
echo -n 'export PATH=~/bin:$PATH' >> ~/.zshrc
or
echo -n 'export PATH=$HOME/bin:$PATH' >> ~/.zshrc
To see the effect, do source ~/.zshrc in the same tab or open a new tab
Added path to ~/.zshrc
sudo vi ~/.zshrc
add new path
export PATH="$PATH:[NEW_DIRECTORY]/bin"
Update ~/.zshrc
Save ~/.zshrc
source ~/.zshrc
Check PATH
echo $PATH
OPTION 1: Add this line to ~/.zshrc:
export "PATH=$HOME/pear/bin:$PATH"
After that you need to run source ~/.zshrc in order your changes to take affect OR close this window and open a new one
OPTION 2: execute it inside the terminal console to add this path only to the current terminal window session. When you close the window/session, it will be lost.
If you are on macOS (I'm on Monterey 12.3.1), you may have been pulling your hair like I did metaphorically. These instructions above all worked for me within the terminal session, but I could never get it to persist no matter what I did with export. Moreover, I couldn't find the .zshrc anywhere.
Turns out Apple does it differently. The file you need to edit is etc/paths. You can simply sudo nano /etc/paths and add your path in a new line. Then simply restart terminal and voila.
for me PATH=$PATH:/path/to/file/bin
then export PATH worked.
to check echo $PATH . other solutions are adding the path temporarily.
I'm on Monterey 12.4 and the only way I could change the path was using the helper function. Editing text files in nano did diddly squat
# append
path+=('/foo/bar/yourpath')
# export to sub-processes
export PATH
to verify your new directory has been added correctly, you can use
print -l $path
thanks to the fact that its type is known to be an array
how to append new plugin to zshrc file. I tried the below syntax but it replaced with new one.
sed -i -e 's/plugins=(.*)/plugins=(zsh-syntax-highlighting)/' ~/.zshrc
I want to add git, file, docker etc.
pl give me the corrected syntax.
KSK

Where default PATH environment variable is defined

I've written a simple experiment which consists of cpp and shell. The main thing that cpp does is execve("./test.sh", NULL, NULL);, and shell tries to output environment variables as follows:
printenv
echo "PATH: $PATH"
It's quite expected that the environment is empty since I've sent no environment variables, but I get the following output:
PWD=/home/user/code/security_playground/display_path
PATH: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
So, first of all, the environment is not empty. Thus, the first question is how the environment is populated? What are defaults and minimal environment you could possibly get and where is it defined (if defined in config, etc.)?
Second question is where did PATH emerge from. It's not printed by printenv, but it's there if I print it with echo, and test.sh is still capable of calling utilities. Brief googling gave me an idea of /etc/environment, but it's contents is different:
└─$ cat /etc/environment
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/local/games:/usr/games
Which, by the way, is surprisingly empty. Why?
I do realize that much of that should be somewhere in docs, but all my googling lead me to quite generic environment descriptions, so any answers or search hints are welcome.
Where default PATH environment variable is defined
It depends. On the distribution, on the shell, on the environment, on OS, on configuration files.
I have some systems at hand, I see:
ArchLinux adds to PATH in /etc/profile.
OpenSuSE has yet a different code that adds to PATH from /etc/profile
CentOS7 has has yet a different code in /etc/profile
I suspect that each distribution places files in slightly different places, they will ship with slightly different configuration.
how the environment is populated?
Bash has DEFAULT_PATH_VALUE that is used when PATH is not set. But don't look at the default value too much - package distrbutors overwrite it.
There is /etc/environment (and pam_env.conf) that are read by PAM on login. So when you login. But not when you chroot.
The is /etc/profile and ~/.profile files read by the shell. These scripts may read other files. So usually there is drop-in dir /etc/profile.d/*.
In bash there is /etc/bashrc or /etc/bash.bashrc /etc/bash.login /etc/bash.logout or similar and user configuration files ``~/.bashrcetc. In bash also/etc/bash_completion.dand all scripts in/usr/share/bash_completionare also sourced, when completion is enabled. BUT these files are for Bourne shell - there are also other shells, notablyzshandcsh`, and people use them and they have different syntax and different set of startup files.
Each of these scripts can manipulate PATH, it can reset it, append to it, do whatever it wants to it.
And there are also different set of files read on login/non-login + interactive/non-interactive shells, and these files can include each other (it's common to have .bash_profile or other files that just source .bashrc or similar).
What are defaults
The /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin looks like a good default that should be safe on any linux.
by the way, is surprisingly empty. Why?
My /etc/environment file is just empty and has only the default comment in linux-pam package. /etc/environment has a very simple syntax and is read by pam (there may be no pam on your system...). To modify shell environment, one would prefer shell files - so /etc/profile is the place to keep system modifications. Also /etc/environment has simple key=value syntax, wherea's in /etc/profile you have, well, shell. And /etc/environment is read on login by PAM - so putting PATH just there would break I think for example docker, it just runs a shell in chroot, there is login.
To quote the POSIX specification:
If PATH is unset or is set to null, the path search is implementation-defined.
One way way an implementation can behave when PATH is initially unset is to simply set it to a hardcoded value. (It could also refuse to run any external executables at all, or throw monkeys at your face; "implementation defined" means the standard takes no position on what the computer does in the circumstance at hand, so it's unwise to make any assumption about what that behavior will be).
Similarly, for PWD, with emphasis added:
The value is set by the cd utility, and by the sh utility during initialization
...so initializing this at shell startup time is explicitly mandated by the standard.

Where is PATH defined in CentOS?

I started looking into files such as:
/etc/profile
~/.bash_profile
etc.
in order to locate where environment variables were defined. Unfortunately, I couldn't locate the $PATH variable. I am using Bash.
The initial PATH environment variable is inherited from ... whatever launched the shell. For example commands like sudo, sshd, whatever creates your shall after a desktop login.
There also appears to be a PATH that is hardwired into the bash binary for cases where an initial PATH is not inherited. (Look at the output from strings /bin/bash.)
Then various shell initialization scripts get a go at setting or updating PATH. For example, on Ubuntu the PATH variable is updated by /etc/profile.d/apps-bin-path.sh ... which is run by /etc/profile.
You should not worry (or even ask) where PATH is set, since you should not be trusting a random distro to put the right directories in the right sequence.
Instead, you set the PATH you need in your shell's profile. That's it.
As a starting point, POSIX mandates that getconf PATH returns the system's default PATH. If you have a $HOME/bin and there's a /usr/local/bin, then you add them.
Here's what this looks like on my machine:
PATH="$(/usr/bin/getconf PATH)"
PATH="$PATH:/usr/sbin"
PATH="$PATH:/usr/local/bin"
PATH="$PATH:$HOME/bin"
With this setup, it's easy to adapt the sequence. Maybe you don't like the ancient vim in /usr/bin/vi? Compile it yourself and move /usr/local/bin to the front.

in which order will ubuntu search bin-folders for executables?

So I learned that in order to be able to execute a program from everywhere in the shell I have to put a reference file looking something like
#!/bin/bash
path/to/my/original/executable
in my bin-directory and make it executable.
On my current linux system (provided by my workplace) there are multiple bin-directories like
/usr/local/bin
/home/MyUsername/bin/
/home/otherUsername/bin/
For my work I have to alter a c++ program which is supposed to be usable for every user on the computer (hence I used /usr/local/bin/ sofar). But I realized that my changes to the original program do not come through... So my questions are:
Where could other bin-folders be, that I need to check for old executables?
How does my operating system (ubuntu 16.04) choose between executables in different bin-locations but with the same name?
Thanks in advance!
The PATH shell variable contains a colon separated list of paths to look for executables in. The list is processed left to right, the shell executes the first executable binary it finds (make sure to chmod +x the binary you are providing). If you want an easier printout you can use: echo $PATH | tr ":" "\n". Also keep in mind that a program might have been started with a different PATH than your shell and that users can customize their PATH variable. Systemwide PATH settings can usually be found in /etc/profile or /etc/profile.d/. You can use which file to display the full path expansion of file.

How can I debug $PATH?

There is a directory in my $PATH that I want removed.
But I cannot figure out how it got there.
I've looked at all the places I can think of where $PATH gets set: .profile, .bashrc, .bash_profile, et cetra
but I cannot find who is putting this particular directory into $PATH.
Clearly, I do not know all the things that change $PATH when my system starts.
Is there a way to debug the startup sequence?
Ideally I just want to set a trap on anything that touches $PATH.
Or may there is a log file (or ordered set of log files) I can scan.
How can I find how each directory gets set in $PATH among all the things that run when my system starts?
There are two ways to do this:
Try bash --login -x in a terminal window. This will print every line of the setup scripts as they are executed (kudos go to kojiro).
Alternatively, you could add set -x near the top of /etc/profile or $HOME/.profile. The danger here is that if you make a mistake, your system may become unusable (if you can't start a new shell anymore) plus you need to be root. If you want to try this, I suggest to create two terminal windows, keep them open at all cost, make the changes and then start new terminal windows to see the effects.
Use grep -r PATTERN / (replace PATTERN with the path that you look for). That should search your whole hard disk for this pattern and can take a long time. Maybe start with your home directory. If you want to search just the . files, use this trick: grep -r PATTERN .??*
If you are using BASH, the way the login loading works is this order:
/etc/profile is first read in.
$HOME/.bash_profile is read in.
If $HOME/.bash_profile doesn't exist, then $HOME/.bash_login is read in.
If $HOME/.bash_login doesn't exist, then $HOME/.profile is read in.
NOTE: That $HOME/.bashrc is not read in by default. However, many people do source in their .bashrc file at login.
When a shell is invoked, and it's not a login shell, then the $HOME/.bashrc file is loaded.
By default, the Mac's PATH is /bin:/usr/bin:/usr/sbin:/sbin. This is set via /etc/profile which executes /usr/libexec/path_helper. This takes the initial paths from /etc/paths.d/... and from /etc/paths. The /etc/paths.d only adds in the /opt/X11/bin directory if X11 is installed. Otherwise, /etc/paths is used and it contains the default paths.
Mine is set to:
/usr/local/bin
/usr/bin
/bin
/usr/sbin
/sbin
I use /usr/local/bin for third party tools. or newer versions of already installed tools. For example, /usr/bin/svn is version 1.7.17 while /usr/local/bin/svn is set to version 1.8.10. Me invoking svn will invoke the version of Subversion I installed, and not the native version.
In this case, the 1.8 version of Subversion is actually installed in /opt/subversion/bin/svn, but I create a symbolic link to it in /usr/local/svn. I do this for any tool that I install: The tool gets installed in /opt and I link the binaries and scripts to /usr/local/bin, so they're in my $PATH.
Place these 2 lines at top of your ~/.bash_profile (create it if it doesn't exist):
set -o functrace
trap '[[ "$BASH_COMMAND" == *" PATH="* ]] && echo "$BASH_COMMAND"' DEBUG
Then exit the current shell and start a new shell.
It will print every time PATH variable is set in your environment.

Resources