Run It from Anywhere: ~/bin on Your PATH
Copy a finished script into ~/bin without its .sh, put ~/bin on your PATH, and it runs by name from any folder like any other command, as long as the paths inside it work from anywhere and its name isn't already taken, which type checks.
Apprentice · Unix · Linux · Updated
After this lesson you can
- Put your own scripts in ~/bin on your PATH so they run by name from any folder, with paths inside them that work from anywhere
- Choose a script name that doesn't clash with an existing command, checking it with type, and drop the .sh
Key takeaways
- A script runs in the folder you're in, not the folder it's saved in, so a path like todo.txt breaks elsewhere. Write ~/terminal-practice/morning/todo.txt instead.
- ~/bin is the usual folder for your own commands. Copy a script there without its .sh, and on your PATH it runs by name from anywhere.
- Ubuntu's ~/.profile adds ~/bin to PATH in every new terminal once the folder exists. On a Mac, add the line to ~/.zshrc.
- Before you use a name, type checks it isn't taken. A script called test never runs, because the shell's own test comes first.
Your morning script works, but only from its own folder, and typing ~/terminal-practice/morning/morning.sh every morning is no shortcut. You want to open a terminal, type morning, and have it run, the way ls and date do. That's what turns a script into a command of your own, and it's how developers and administrators keep the dozens of small tools they rely on a word away.
A script runs where you are
Start from your home folder and run the script by its full path:
cd ~
~/terminal-practice/morning/morning.sh
Good morning, sam!
Today is Monday, November 09.
To do:
cat: todo.txt: No such file or directory
Free disk space:
Filesystem Size Used Avail Use% Mounted on
/dev/sdd 1007G 31G 926G 4% /
Everything worked except the list. A script runs in the folder you're in, not the folder it's saved in, so cat todo.txt looked for todo.txt in your home folder. It's like leaving a note that says "the key is in the drawer" without saying which room.
The fix is a path that works from anywhere: one that starts with ~ (Moving Around: cd, .. and ~). Open the script in nano and change the cat line to this:
cat ~/terminal-practice/morning/todo.txt
Run it again from your home folder, and the list is back:
~/terminal-practice/morning/morning.sh | head -n 7
Good morning, sam!
Today is Monday, November 09.
To do:
pay the phone bill
finish the history essay
buy coffee beans
~/bin: a folder for your own commands
PATH: How a Command Is Found showed that the shell finds a command by searching the folders on your PATH. So give your scripts a folder of their own and put it on the list. The usual name is ~/bin, read "home bin", short for binaries, the old word for programs:
mkdir -p ~/bin
cp ~/terminal-practice/morning/morning.sh ~/bin/morning
ls -l ~/bin
total 4
-rwxr-xr-x 1 sam sam 290 Nov 9 06:09 morning
The copy is called morning, without .sh. Nobody types ls.sh: a command's name is what you type, and the ending only matters to editors. cp kept the x, so it may run. Now put ~/bin on your PATH and try it from anywhere:
export PATH="$HOME/bin:$PATH"
which morning
cd /tmp
morning | head -n 3
/home/sam/bin/morning
Good morning, sam!
Today is Monday, November 09.
morning, from any folder, by name. When you change morning.sh later, copy it into ~/bin again, or link it instead with ln -s (Shortcuts to Files: ln -s) so ~/bin always runs the latest.
Keep it on your PATH
The export line lasts until you close the window. On Ubuntu you don't need to add it anywhere: Ubuntu's own ~/.profile already has these lines, which put ~/bin at the front of PATH whenever the folder exists:
grep -A 2 'HOME/bin"' ~/.profile
if [ -d "$HOME/bin" ] ; then
PATH="$HOME/bin:$PATH"
fi
~/.profile runs when you sign in. In WSL, every new Ubuntu tab signs in, so open a new tab and morning works there. On Ubuntu's desktop, sign out and back in once.
On a Mac, zsh doesn't read ~/.profile. Back up ~/.zshrc, add this line at the end, and open a new window (Startup Files: .bashrc, .zshrc and source):
export PATH="$HOME/bin:$PATH"
Names that don't clash
~/bin comes first on PATH, so a script there wins over a program with the same name. But aliases, functions and the shell's own builtins come before PATH altogether. Before you name a script, ask type what the name already means:
type test
type morning
type evening
test is a shell builtin
morning is /home/sam/bin/morning
bash: type: evening: not found
not found means the name is free. test is taken, by a shell builtin, and this is what happens to a script with that name:
cp ~/bin/morning ~/bin/test
test
type -a test
rm ~/bin/test
test is a shell builtin
test is /home/sam/bin/test
test is /usr/bin/test
test is /bin/test
test printed nothing: bash ran its own builtin, and your script never started. type -a lists every test in the order the shell checks them, and yours isn't first. A few habits that avoid this:
- Check with
typefirst, and pick a name that saysnot found. - Use lowercase and hyphens, such as
morningorbackup-photos, never spaces. - Drop the
.shon the copy in~/bin, and keep it on the file you edit.
Check yourself
2 questions to try on the lesson page.
So what
A folder on your PATH, a name nobody else uses, and paths that work from anywhere: your script is now a command, as much a part of your computer as ls.