Add a relative path to $PATH on fish startup - macos

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.

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
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).
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

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.

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

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

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.

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:
This is setting $PATH directly, just as with other shells. This won't persist across shells.
This checks that it's not already in the $PATH, so that sub-shells won't get longer, redundant entries in $PATH

Related

Why do I have to type ./ before gradlew in others' terminals and not in command prompt (Windows) when I run Spring Boot? [duplicate]

When running scripts in bash, I have to write ./ in the beginning:
$ ./manage.py syncdb
If I don't, I get an error message:
$ manage.py syncdb
-bash: manage.py: command not found
What is the reason for this? I thought . is an alias for current folder, and therefore these two calls should be equivalent.
I also don't understand why I don't need ./ when running applications, such as:
user:/home/user$ cd /usr/bin
user:/usr/bin$ git
(which runs without ./)
Because on Unix, usually, the current directory is not in $PATH.
When you type a command the shell looks up a list of directories, as specified by the PATH variable. The current directory is not in that list.
The reason for not having the current directory on that list is security.
Let's say you're root and go into another user's directory and type sl instead of ls. If the current directory is in PATH, the shell will try to execute the sl program in that directory (since there is no other sl program). That sl program might be malicious.
It works with ./ because POSIX specifies that a command name that contain a / will be used as a filename directly, suppressing a search in $PATH. You could have used full path for the exact same effect, but ./ is shorter and easier to write.
EDIT
That sl part was just an example. The directories in PATH are searched sequentially and when a match is made that program is executed. So, depending on how PATH looks, typing a normal command may or may not be enough to run the program in the current directory.
When bash interprets the command line, it looks for commands in locations described in the environment variable $PATH. To see it type:
echo $PATH
You will have some paths separated by colons. As you will see the current path . is usually not in $PATH. So Bash cannot find your command if it is in the current directory. You can change it by having:
PATH=$PATH:.
This line adds the current directory in $PATH so you can do:
manage.py syncdb
It is not recommended as it has security issue, plus you can have weird behaviours, as . varies upon the directory you are in :)
Avoid:
PATH=.:$PATH
As you can “mask” some standard command and open the door to security breach :)
Just my two cents.
Your script, when in your home directory will not be found when the shell looks at the $PATH environment variable to find your script.
The ./ says 'look in the current directory for my script rather than looking at all the directories specified in $PATH'.
When you include the '.' you are essentially giving the "full path" to the executable bash script, so your shell does not need to check your PATH variable. Without the '.' your shell will look in your PATH variable (which you can see by running echo $PATH to see if the command you typed lives in any of the folders on your PATH. If it doesn't (as is the case with manage.py) it says it can't find the file. It is considered bad practice to include the current directory on your PATH, which is explained reasonably well here: http://www.faqs.org/faqs/unix-faq/faq/part2/section-13.html
On *nix, unlike Windows, the current directory is usually not in your $PATH variable. So the current directory is not searched when executing commands. You don't need ./ for running applications because these applications are in your $PATH; most likely they are in /bin or /usr/bin.
This question already has some awesome answers, but I wanted to add that, if your executable is on the PATH, and you get very different outputs when you run
./executable
to the ones you get if you run
executable
(let's say you run into error messages with the one and not the other), then the problem could be that you have two different versions of the executable on your machine: one on the path, and the other not.
Check this by running
which executable
and
whereis executable
It fixed my issues...I had three versions of the executable, only one of which was compiled correctly for the environment.
Rationale for the / POSIX PATH rule
The rule was mentioned at: Why do you need ./ (dot-slash) before executable or script name to run it in bash? but I would like to explain why I think that is a good design in more detail.
First, an explicit full version of the rule is:
if the path contains / (e.g. ./someprog, /bin/someprog, ./bin/someprog): CWD is used and PATH isn't
if the path does not contain / (e.g. someprog): PATH is used and CWD isn't
Now, suppose that running:
someprog
would search:
relative to CWD first
relative to PATH after
Then, if you wanted to run /bin/someprog from your distro, and you did:
someprog
it would sometimes work, but others it would fail, because you might be in a directory that contains another unrelated someprog program.
Therefore, you would soon learn that this is not reliable, and you would end up always using absolute paths when you want to use PATH, therefore defeating the purpose of PATH.
This is also why having relative paths in your PATH is a really bad idea. I'm looking at you, node_modules/bin.
Conversely, suppose that running:
./someprog
Would search:
relative to PATH first
relative to CWD after
Then, if you just downloaded a script someprog from a git repository and wanted to run it from CWD, you would never be sure that this is the actual program that would run, because maybe your distro has a:
/bin/someprog
which is in you PATH from some package you installed after drinking too much after Christmas last year.
Therefore, once again, you would be forced to always run local scripts relative to CWD with full paths to know what you are running:
"$(pwd)/someprog"
which would be extremely annoying as well.
Another rule that you might be tempted to come up with would be:
relative paths use only PATH, absolute paths only CWD
but once again this forces users to always use absolute paths for non-PATH scripts with "$(pwd)/someprog".
The / path search rule offers a simple to remember solution to the about problem:
slash: don't use PATH
no slash: only use PATH
which makes it super easy to always know what you are running, by relying on the fact that files in the current directory can be expressed either as ./somefile or somefile, and so it gives special meaning to one of them.
Sometimes, is slightly annoying that you cannot search for some/prog relative to PATH, but I don't see a saner solution to this.
When the script is not in the Path its required to do so. For more info read http://www.tldp.org/LDP/Bash-Beginners-Guide/html/sect_02_01.html
All has great answer on the question, and yes this is only applicable when running it on the current directory not unless you include the absolute path. See my samples below.
Also, the (dot-slash) made sense to me when I've the command on the child folder tmp2 (/tmp/tmp2) and it uses (double dot-slash).
SAMPLE:
[fifiip-172-31-17-12 tmp]$ ./StackO.sh
Hello Stack Overflow
[fifi#ip-172-31-17-12 tmp]$ /tmp/StackO.sh
Hello Stack Overflow
[fifi#ip-172-31-17-12 tmp]$ mkdir tmp2
[fifi#ip-172-31-17-12 tmp]$ cd tmp2/
[fifi#ip-172-31-17-12 tmp2]$ ../StackO.sh
Hello Stack Overflow

Script Shell: How to use a script that I created when I'm not in the directory he is

I created a mv_file.sh script and I want to use it even if I'm not in the path where the script is located.
My question is: What do I have to do to use ./mv_file instead of mv, and this whatever the path I'm in.
You can add the directory, where your script is, to your path. Open ~/.bash_profile (or whatever you choose to use, this sometimes in ~/.bashrc or in ~/.zshrc if you use zsh for example) And add:
PATH=/path/to/script:$PATH
OR you can add the script to a directory that's already in your path. For gnu/linux for example that might be:
$HOME/bin
To see what you're current PATH is:
echo $PATH
If memory serves, this should work just as well on a mac.

Run shell script with external utility (plink) - unable to provide the context [duplicate]

When running scripts in bash, I have to write ./ in the beginning:
$ ./manage.py syncdb
If I don't, I get an error message:
$ manage.py syncdb
-bash: manage.py: command not found
What is the reason for this? I thought . is an alias for current folder, and therefore these two calls should be equivalent.
I also don't understand why I don't need ./ when running applications, such as:
user:/home/user$ cd /usr/bin
user:/usr/bin$ git
(which runs without ./)
Because on Unix, usually, the current directory is not in $PATH.
When you type a command the shell looks up a list of directories, as specified by the PATH variable. The current directory is not in that list.
The reason for not having the current directory on that list is security.
Let's say you're root and go into another user's directory and type sl instead of ls. If the current directory is in PATH, the shell will try to execute the sl program in that directory (since there is no other sl program). That sl program might be malicious.
It works with ./ because POSIX specifies that a command name that contain a / will be used as a filename directly, suppressing a search in $PATH. You could have used full path for the exact same effect, but ./ is shorter and easier to write.
EDIT
That sl part was just an example. The directories in PATH are searched sequentially and when a match is made that program is executed. So, depending on how PATH looks, typing a normal command may or may not be enough to run the program in the current directory.
When bash interprets the command line, it looks for commands in locations described in the environment variable $PATH. To see it type:
echo $PATH
You will have some paths separated by colons. As you will see the current path . is usually not in $PATH. So Bash cannot find your command if it is in the current directory. You can change it by having:
PATH=$PATH:.
This line adds the current directory in $PATH so you can do:
manage.py syncdb
It is not recommended as it has security issue, plus you can have weird behaviours, as . varies upon the directory you are in :)
Avoid:
PATH=.:$PATH
As you can “mask” some standard command and open the door to security breach :)
Just my two cents.
Your script, when in your home directory will not be found when the shell looks at the $PATH environment variable to find your script.
The ./ says 'look in the current directory for my script rather than looking at all the directories specified in $PATH'.
When you include the '.' you are essentially giving the "full path" to the executable bash script, so your shell does not need to check your PATH variable. Without the '.' your shell will look in your PATH variable (which you can see by running echo $PATH to see if the command you typed lives in any of the folders on your PATH. If it doesn't (as is the case with manage.py) it says it can't find the file. It is considered bad practice to include the current directory on your PATH, which is explained reasonably well here: http://www.faqs.org/faqs/unix-faq/faq/part2/section-13.html
On *nix, unlike Windows, the current directory is usually not in your $PATH variable. So the current directory is not searched when executing commands. You don't need ./ for running applications because these applications are in your $PATH; most likely they are in /bin or /usr/bin.
This question already has some awesome answers, but I wanted to add that, if your executable is on the PATH, and you get very different outputs when you run
./executable
to the ones you get if you run
executable
(let's say you run into error messages with the one and not the other), then the problem could be that you have two different versions of the executable on your machine: one on the path, and the other not.
Check this by running
which executable
and
whereis executable
It fixed my issues...I had three versions of the executable, only one of which was compiled correctly for the environment.
Rationale for the / POSIX PATH rule
The rule was mentioned at: Why do you need ./ (dot-slash) before executable or script name to run it in bash? but I would like to explain why I think that is a good design in more detail.
First, an explicit full version of the rule is:
if the path contains / (e.g. ./someprog, /bin/someprog, ./bin/someprog): CWD is used and PATH isn't
if the path does not contain / (e.g. someprog): PATH is used and CWD isn't
Now, suppose that running:
someprog
would search:
relative to CWD first
relative to PATH after
Then, if you wanted to run /bin/someprog from your distro, and you did:
someprog
it would sometimes work, but others it would fail, because you might be in a directory that contains another unrelated someprog program.
Therefore, you would soon learn that this is not reliable, and you would end up always using absolute paths when you want to use PATH, therefore defeating the purpose of PATH.
This is also why having relative paths in your PATH is a really bad idea. I'm looking at you, node_modules/bin.
Conversely, suppose that running:
./someprog
Would search:
relative to PATH first
relative to CWD after
Then, if you just downloaded a script someprog from a git repository and wanted to run it from CWD, you would never be sure that this is the actual program that would run, because maybe your distro has a:
/bin/someprog
which is in you PATH from some package you installed after drinking too much after Christmas last year.
Therefore, once again, you would be forced to always run local scripts relative to CWD with full paths to know what you are running:
"$(pwd)/someprog"
which would be extremely annoying as well.
Another rule that you might be tempted to come up with would be:
relative paths use only PATH, absolute paths only CWD
but once again this forces users to always use absolute paths for non-PATH scripts with "$(pwd)/someprog".
The / path search rule offers a simple to remember solution to the about problem:
slash: don't use PATH
no slash: only use PATH
which makes it super easy to always know what you are running, by relying on the fact that files in the current directory can be expressed either as ./somefile or somefile, and so it gives special meaning to one of them.
Sometimes, is slightly annoying that you cannot search for some/prog relative to PATH, but I don't see a saner solution to this.
When the script is not in the Path its required to do so. For more info read http://www.tldp.org/LDP/Bash-Beginners-Guide/html/sect_02_01.html
All has great answer on the question, and yes this is only applicable when running it on the current directory not unless you include the absolute path. See my samples below.
Also, the (dot-slash) made sense to me when I've the command on the child folder tmp2 (/tmp/tmp2) and it uses (double dot-slash).
SAMPLE:
[fifiip-172-31-17-12 tmp]$ ./StackO.sh
Hello Stack Overflow
[fifi#ip-172-31-17-12 tmp]$ /tmp/StackO.sh
Hello Stack Overflow
[fifi#ip-172-31-17-12 tmp]$ mkdir tmp2
[fifi#ip-172-31-17-12 tmp]$ cd tmp2/
[fifi#ip-172-31-17-12 tmp2]$ ../StackO.sh
Hello Stack Overflow

export, bash command not found

I have used export before but I don't know why when I set the variable PATH to any directory this time, ls, awk commands are not found but no problem with pwd, cd
export PATH="/Users/carolW/Desktop"
ls
-sh: ls: command not found
Use:
export PATH=/Users/carolW/Desktop:$PATH
You're removing all the normal directories from your path, so it only looks in your Desktop folder for everything. You just want to add your directory, not replace the entire path with it.
most probably because pwd and cd are built shell command
(you can test:
which pwd
which ls
)
However, ls are such are tools you can find in /bin directory or such, and those paths are defined in your variable PATH.
If you clear the variable PATH, most likely you won't find your tool anymore.
You may use export PATH=$PATH:"/Users/carolW/Desktop"
So that you concatenate your path to the already existing paths

How to remove entry from $PATH on mac

I was trying to install Sencha Touch SDK tools 2.0.0 but could not run it properly. It created an entry in the $PATH variable.
Later I deleted the sencha sdk tools folder but didn't realize that the path variable is still there.
When i did echo $PATH I got -
/Applications/SenchaSDKTools-2.0.0-beta3:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin:/usr/X11/bin
I searched on how to remove variables from $PATH and followed these steps :
Gave the command PATH="/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin:/usr/X11/bin"
Did echo $PATH which showed /usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin:/usr/X11/bin
gave the command export PATH
Closed terminal and reopened it. Gave the command echo $PATH. This time I got
/Applications/SenchaSDKTools-2.0.0-beta3:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin:/usr/X11/bin
Can anyone tell me what am i doing wrong?
echo $PATH and copy it's value
export PATH=""
export PATH="/path/you/want/to/keep"
Check the following files:
/etc/bashrc
/etc/profile
~/.bashrc
~/.bash_profile
~/.profile
~/.MacOSX/environment.plist
Some of these files may not exist, but they're the most likely ones to contain $PATH definitions.
On MAC OS X Leopard and higher
cd /etc/paths.d
There may be a text file in the above directory that contains the path you are trying to remove.
vim textfile //check and see what is in it when you are done looking type :q
//:q just quits, no saves
If its the one you want to remove do this
rm textfile //remove it, delete it
Here is a link to a site that has more info on it, even though it illustrates 'adding' the path. However, you may gain some insight.
What you're doing is valid for the current session (limited to the terminal that you're working in). You need to persist those changes. Consider adding commands in steps 1-3 above to your ${HOME}/.bashrc.
If you're removing the path for Python 3 specifically, I found it in ~/.zprofile and ~/.zshrc.
$PATH contains data that is referenced from actual files. Ergo, you should find the file containing the reference you want to delete, and then delete said reference.
Here is a good list to run through progressively [copied from #Ansgar's answer with minor updates].
/etc/bashrc
/etc/profile
~/.bashrc
~/.bash_profile
~/.profile
~/.MacOSX/environment.plist
/etc/paths
/etc/paths.d/
Note that /etc/paths.d/ is a directory that contains files with path references. For example, inside this directory may be a file called, say, fancy-app, and inside this file you'll see an entry like below:
/path/to/fancy-app
This path will appear in your $PATH and you can delete the entry in the file to remove it, or you can delete the file if it has only the one reference you want to remove.
Use sudo pico /etc/paths inside the terminal window and change the entries to the one you want to remove, then open a new terminal session.
when you login, or start a bash shell, environment variables are loaded/configured according to .bashrc, or .bash_profile. Whatever export you are doing, it's valid only for current session. so export PATH=/Applications/SenchaSDKTools-2.0.0-beta3:$PATH this command is getting executed each time you are opening a shell, you can override it, but again that's for the current session only. edit the .bashrc file to suite your need. If it's saying permission denied, perhaps the file is write-protected, a link to some other file (many organisations keep a master .bashrc file and gives each user a link of it to their home dir, you can copy the file instead of link and the start adding content to it)
Close the terminal(End the current session). Open it again.
If the manual export $PATH method does not seem to be working after you close the terminal and open again, definitely check the shell configuration files.
I found a small script that kept adding some more path in front of the $PATH everytime it was open.
For zsh you can check the ~/.zshrc file.

Resources