How to Run a Bash Script From Anywhere on Your Apple Computer

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.

Pinterest-geared image shows two screenshots (also shown below), my post title, and my main URL.

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 sh or 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.

Image shows the 'sh' file being created, executed with sh, unable to execute with the filename only, being chmod to be executable, and working.
Here you can see a file created with touch which isn’t executable so you need to use sh on it to run it. Running chmod u+x against it converts the file to be executable, confirmed with stat for status, and now I can run it by just calling the file’s name.

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:

Image shows two commands in my terminal. The first matched an executable file existing in my bin directory and in my $PATH so it executed. The second was not so I received an unknown command error.

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 /Users/kyra/bin, /usr/local/bin, /System/Cryptexes/App/usr/bin, /usr/bin, /bin, /usr/sbin, /sbin, and /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 vim and 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 vim, 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.

Image is a screenshot of Real Python's post How to Run Your Python Scripts showing using the python command. Specifically for the shell script showing "python3 hello.py" and it's results.
Screenshot taken on January 19th, 2023 from Real Python’s post How to Run Your Python Scripts showing how they just need to type in python3 and the computer knows to execute 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.

Image shows three screenshots from my terminal all showing the contents of different directories whose pathway is listed in the PATH variable.

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

Here the 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 $PATH list…


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:

vim ~/.config/fish/config.fish

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.

Image is a screenshot from warp's What happens when you open a terminal and enter ‘ls’ article showing a diagram showcasing what happens when the user inputs characters into the terminal on their computer.
This screenshot was taken from warp’s What happens when you open a terminal and enter ‘ls’ article showcasing the steps your computer goes through when taking the input.

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.



Related Posts

Latest Posts