How to Run a Bash Script From Anywhere on Your Apple Computer
Once I started blogging I found I needed to compress my images, so they’d be smaller, before uploading them to my website. Over time I realized this could be done so much quicker if I put each single blog post’s images into its own directory (folder) and run a Bash script to compress all of the current images at once. This was amazing but quickly became cumbersome once again when I realized I had two options; either to move my script to each new directory of images each time I ran it or run the script from one place and pass in the location of the images as an argument. I had gone with the first option which meant I had to navigate to where I want the script to be, move it, and run it… then move it into another directory and run it again… and again and again. This was much too troublesome; I wanted it streamlined so I could run my script from anywhere freeing me to compress and, later, watermark my images at will.
Quick Aside: Don’t Forget to Make Your Script Executable
When I first started working with bash scripts I saw, by default, they could only be run by using the
bash command unless I first made them executable, using
chmod, in which case I could run them by simply calling their filename. I mentioned this as below I use executable files and
sh isn’t used.
Additionally, I use iTerm2 (Build 3.4.19) so my terminal may look different from yours.
So How Can I Run a Script From Anywhere?
When you run a command in your terminal the computer looks through a list of directories where the file might reside and, if found, executes it. This special list of directories is stored in the
PATH environment variable on your computer. If your shell goes through all of these paths, in order, and can’t find the file it then lets you know that it can’t be found before exiting. If there are two matching files it will run whichever one it comes across first.
To show this I opened my terminal and ran one of the scripts I had previously created and put into a directory whose path was included in my
PATH variable. As such it was found and executed. For contrast I next ran a file that doesn’t exist so this time the terminal returned
fish: Unknown command: thisIsNotInPath. Here’s a screenshot of these two attempts:
To see what pathways are in your environment variable you just need to open your terminal and type in
echo $PATH. The echo command prints out the arguments as standard output thus showing it to you the user. The
$ signifies a variable and
PATH is this variable name. When I run this command on my computer I get:
❯ echo $PATH /Users/kyra/bin /usr/local/bin /System/Cryptexes/App/usr/bin /usr/bin /bin /usr/sbin /sbin /Library/Apple/usr/bin
This means when I execute a command my computer looks through these eight different directories (specifically
/Library/Apple/usr/bin) one by one to find that file. It does this in order so if you have two files with the same name it will always execute the first one it comes across each time.
The directory I manually added to the
PATH and use is
/Users/kyra/bin. This is the directory where I put all of the scripts I want to run from anywhere. So for the below example I created a new file named
testing.sh and, for it’s contents, had it say
echo "Hello Readers" so when the file is eventually executed I’ll see
Hello Readers outputted. Here it is:
// First I called a file that doesn't exist to // show you the output ❯ neverCreated.sh fish: Unknown command: neverCreated.sh // I then call the script I just created. It // prints out, echoes, the words "Hello Readers". // At this time the script isn't an executable // but it can be found ❯ testing.sh fish: Unknown command. '/Users/kyra/bin/testing.sh' exists but is not an executable file. // If I start with sh it works great and outputs // the message. ❯ sh testing.sh Hello Readers // I can easily chmod it and make it executable // so now... ❯ chmod u+x bin/testing.sh // I can now call it and it works. ❯ testing.sh Hello Readers // To test this I navigate elsewhere on my // computer.... ❯ cd Desktop/temp/ // And call it again. It works as my computer // looked through my paths and found it in the // bin directory. ~/Desktop/temp ❯ testing.sh Hello Readers
And with that you can run your script from anywhere on your computer and you don’t need to navigate into the
bin directory at all!
How Is This Useful?
In my case I create a new directory each time I start planning a new blog post idea. I start by adding all the images I may want to use in my post, go through them individually to remove any excess ones, crop the remaining ones, manually create a Pinterest and Instagram image on Canva, and then compress these final remaining ones. To compress them I now only need to open a terminal to this directory and execute my script to have all my compressed images ready to upload. Before putting my script in the
bin directory the compressing step took longer and was way more cumbersome than I wanted… now it’s simply a quick footnote to my process. If you’re more interested in the script itself than how I execute it you can read about it in my previous post Use a Simple Bash Script to Resize Your Images Quickly and Easily or you can check it out on GitHub.
I also used this technique for my
nvim. Right now when I want to edit a file in the terminal I type
vim filename.extension but in reality I have a symbolic link in my
bin directory (where it can be found and so executed from anywhere) that links to
/usr/local/bin/nvim and runs
neoVim instead. It’s a simple trick so I could continue using muscle memory to open my editor without needing to type something else in.
Less personally but more pivotal is the way your computer already uses this. If you navigate to all those pathways listed in your
$PATH you’ll see all the files and programs that can be found and executed from anywhere. One quick example is
python, or a myriad of all the programs you can run from anywhere in the terminal. Using
Python, for instance, you can run your
py files by typing in
python filename.py and your computer will know to run the filename using python.
These are the main reasons I personally use this but I’d love to know more ways that work for you so feel free to share in the comments at the bottom.
But don’t just take my word for this. If you browse through all the directories listed under your $PATH variable you can see all the programs and files that also use this.
What if I Want My Script to Live Somewhere Else but Still Be Called From Anywhere?
If you want your script to live somewhere outside the list of directories in your
PATH variable you could always create a symbolic link so it thinks it’s in one of those directories. In my case my computer sees two of my scripts in my
bin/ directory yet only one technical resides there. The other one actually lives in my
Development/scripts/ directory but is symbolically linked so the computer sees it in the
bin/ directory. When I navigate to the
bin/ directory and list the contents, using
-l to show the long list (link to StackExchange for more information), I see this:
~/bin ❯ ls -la total 8 drwxr-xr-x 5 kyra staff 160 Jan 19 11:37 ./ drwxr-xr-x+ 32 kyra staff 1024 Jan 19 11:07 ../ lrwxr-xr-x 1 kyra staff 59 Oct 12 2021 compress_my_images.sh@ -> /Users/kyra/Development/scripts/compress_my_images.sh -rwxr-xr-x 1 kyra staff 1504 Jan 11 10:24 createAppIcons.sh* lrwxr-xr-x 1 kyra staff 19 Oct 12 2021 vim@ -> /usr/local/bin/nvim
createAppIcons.sh file lives in the
bin/ directory while my
compress_my_images.sh script and
vim call doesn’t. The shell can see it in the
bin directory and follow it to where it really lives (after the arrow).
I talked about creating symbolic links before in my reMarkable template post under Simplify the Process With a Symbolic Link. Essentially, to create the symbolic link I open the terminal and use the link command,
ln, followed by the
-s parameter to specify that I want it to be a symbolic link. I next follow it with the target pathway, where the linked file is located, and then follow that with the location and filename I want to use for that link. So, for instance, in this below example I create an empty script on my
Desktop/temp/ directory and then link it to the
bin directory. I can confirm it was created using the
ls command and passing in the
l parameter to get more information. Here’s what I did:
// Use the touch command to create an // empty script in my temp directory // on my Desktop ❯ touch ~/Desktop/temp/testingThis.sh // Create the symbolic link making sure // to pass in the target file we just // created followed by what I want it // to be called in the bin directory. ❯ ln -s ~/Desktop/temp/testingThis.sh bin/testingHere.sh // To confirm this worked can run ls -l // to see all the files and, if symbolic // linking, what they link to. ❯ ls -l bin/ total 8 lrwxr-xr-x 1 kyra staff 59 Oct 12 2021 compress_my_images.sh@ -> /Users/kyra/Development/myBashScripts/compress_my_images.sh -rwxr-xr-x 1 kyra staff 1504 Jan 11 10:24 createAppIcons.sh* lrwxr-xr-x 1 kyra staff 39 Jan 19 15:50 testingHere.sh@ -> /Users/kyra/Desktop/temp/testingThis.sh lrwxr-xr-x 1 kyra staff 19 Oct 12 2021 vim@ -> /usr/local/bin/nvim
But what if you have a whole directory of scripts you want to run from anywhere and don’t want to move or symbolically link them to a directory in the
What if I Want to Add to $Path’s List of Directories?
Back when I was getting my compression script to run from anywhere I decided to add my
bin/ directory to the list of directories in my
$PATH. I accomplished this by editing my
config.fish file in my
.config/fish/ directory (as I’m using the
Fish terminal emulator) by doing:
Then at the bottom of my file I added one line of text:
set PATH $HOME/bin $PATH
And this is how I added my
bin/ directory to be recognized.
I haven’t had to do this in any other way so in case you don’t use
fish or want a different method I figured I’d share some other links that look simple to follow for either adding a directory to
PATH temporarily, in the current session, or more permanently. These are TechRepublic: Linux 101: What is the Linux $PATH?, How-To Geek: How to Add a Directory to Your $PATH in Linux, PhoenixNAP: Linux: Add a Directory to PATH, or My Cyber Universe: Create a personal bin directory (~/bin) and run scripts without specifying their full path.
Some More Information…
Looking into this more Warp gives great information if you’re more interested in how the terminal works and, specifically, what happens when you open a terminal and enter ‘ls’. Their linked Bash Aliases was also interesting. SysAdvent’s down the rabbit hole with ‘ls’ seems more in depth although I haven’t spent as much time reading but sharing so I can also go back to it in the future.
I hope this was able to help you move your script, or other file, so it was recognizable by your computer. If it helped I’d love to hear how in the comments below. If I missed something you think would be helpful feel free to share that too. I’m sure it will help someone.
Hope you’re having a great day!
If you’re interested in getting any of my future blog updates I currently share them to my Facebook page and Instagram account. You’re also more than welcome to join my email list located right under the search bar or underneath this post.