loading...
Cover image for Bash Script Tool Kit

Bash Script Tool Kit

jimmymcbride profile image Jimmy McBride Updated on ・4 min read

In my blog Write A Bash Script 101, I write about how to make a simple bash script from scratch. The purpose of this blog is to serve as a reference (or tool kit) of different bash commands, that you can use as you start creating new bash scripts, and to help open the door of endless possibilities of productivity the world of bash scripts has to offer.

Pushd And Popd

I go over pushd and popd in this blog about alias's. Alias are a great way to basically write one line bash scripts, so I recommend checking it out if you are unfamiliar with bash alias's. Pushd and popd offer an alternative to cd that have a history you can pop backwards through. Although, it's doesn't have to completely replace cd.

Say your in ~/Downloads directory and you pushd ~/Documents/projects/my-project and then you want to go back to just the projects directory. If you use popd then it will take you back to the Downloads directory, so if you want to go to projects directory you have 2 choices:

  • pushd ~/Documents/projects We have to use an absolute path here because we don't have a way to just move up a directory. Pro you keep the pushd history going. Con you have to type a little more.
  • cd .. You move out of the directory into the parent directory. Pro easy to type. Con you break the pushd history chain. > Note: If you have an alias for the projects directory because you use it so often, when you're in your actual project folder you can just type the alias for that projects directory to bring you back and keep the pushd history chain going without typing the whole absolute path.

Absolute vs Relative Paths

If you're in your projects directory and you cd my-project/ or pushd my-project you are typing a relative path. A relative path is a path that is relative to the current directory you are in.

An absolute path would look like; cd ~/Documents/projects/my-project/ or pushd ~/Documents/projects/my-project/. This will take you to you my-projects directory no matter where you are in your filesystem, which can be really powerful when used in bash scripts.

Variables

Variables are in many coding languages. In bash variables are usually named in all caps and a $ is called in front to let bash know it's a variable.

Declaring a variable:

Typical syntax for declaring a variable:

NAME="Jimmy McBride"

Now if you were to save that to your .bashrc file and reload your terminal and type in echo "$NAME" it should return "Jimmy McBride".

Bash variables:

Bash comes with a few variables already that you can use. Here are some I use the most:

  • $PATH: returns to you all the paths to bin folders you can use to put your bash scripts in.
  • $HOME: returns to you your home route. Usually looks like home/username
  • $USERNAME: returns your username.
  • $PWD: returns your Present Working Directory
  • $1, $2, etc: When you type the name of you bash script it runs the file. You can also type in extra arguments. If you do $1 becomes the first argument you typed after the name of your script and so on.

Example:

File name: new-project

#!/bin/bash

# Push to projects directory with an absolute path
pushd ~/Documents/projects

# Run create-react-app in projects directory
yarn create react-app "$1"

# Move inside the new project folder with a relative path
pushd "$1"

# Open project in VS Code
code .

# Start server
yarn start

Here we can type new-project my-project and it will create a new react app with the name of our first argument ('my-project' in this case) and then move inside that directory, open the project in VS Code and start the server.

Since we used an absolute path at the beginning of our script, it will take us to the projects directory from anywhere in our file system, where we use this command and run create-react-app in the appropriate directory. We only need to use a relative path to move inside the project directory because we are already in the folder we need to be in.

Copy or Move Files and Directories

Syntax for moving files:

mv <target> <destination>
# or
mv <target> <target> <destination>

# Examples

# Moves file1 into folder1
mv file1 folder1

# Moves file1 and file2 into folder1
mv file1 file2 folder1

# Moves folder1 and file1 into folder2
mv folder1 file2 folder1

The syntax for copying files and folders is:

cp <target> <destination>

# Copies file1 into folder1
cp file1 folder1

# If you want to copy an empty folder somewhere:
cp folder1 folder2

# If the folder is not empty, you want to copy it recursively:
cp -r folder1 folder2

Conclusion

These are just a few of the many tools bash has to offer when writing your scripts. I've found that I see myself using these commands the most. As I learn more and get better at writing bash script I will update this blog so be sure to add it to your reading list! What are some of your favorite bash commands you use? Would love to here your thoughts and opinions in the comments below!

Posted on by:

jimmymcbride profile

Jimmy McBride

@jimmymcbride

I'm a left brain artist who loves to learn and explore new things.

Discussion

markdown guide
 

I've never been a fan of pushd for directory stacks, mostly because I get confused with more than one place to return to (which I'll use cd - for) so the benefit is probably lost on me. But one place I wouldn't use them is inside a script, because unless you're careful and trap things to clean up nicely, it'll mess you up if your script exits early.

 

I've been using them in mine and haven't had any issues so far. What kind of problems can using pushd in a script cause? Also, how do you trap things to clean up when using pushd in your scripts?

 

What it is, is that if I'm in a "non-trivial" script, and I'm using pushd and popd to keep a stack of where I am, and one function fails - for example, if pushd doesn't succeed because the directory is missing - then all subsequent commands will think I'm in the wrong place and potentially do destructive things.

On the face of it, cd has the same pitfalls, and if everything was defensively programmed, it wouldn't matter:

if ! cd "$path"; then
  # do some recovery
fi

if ! push "$path"; then
  # do some recovery
fi

But if that's what we're doing, we might as well use absolute paths and program defensively everywhere.

By trapping I mean things like this:

trap clean_up EXIT

clean_up() {
  # do whatever we need to put the system back where we left it.
}

Given that scripts can be sourced as well as executed, changing directory within a script might or might not change the directory of the calling shell.

Sick! That's some pretty cool stuff. I'm going to look into that some more. Thank you for sharing!

 
$ NAME="Jimmy     McBride"
$ echo $NAME
Jimmy McBride
$ echo "$NAME"
Jimmy     McBride

Use quotes!

 

Fair enough. I see your point.

 

I have tried pushd .. to move up one directory level without breaking the pushd/popd chain, and it seems to work great. I’m using GNU bash, version 5.0.7(1)-release

 

That's really great news! I'm going to give it a shot!

 

It might be worth mentioning that whilst push and pop sound lovely they are a really bad idea to use. You should always use relative or absolute paths and not change directories.

 

Well you change directories with cd, that's what cd means, change directory. I haven't run into any problems with pushd/popd at all in my experience. I use them in my alias's and my bash scripts and they've done perfectly fine so far. When you use pushd you are always using a relative or an absolute path.

 

What’s the reasoning behind that? What pitfalls does this help avoid?

 

So there are several problems:

What happens if you call a function that changes the working directory to something like '/'.

Your code thinks that you are in /home/me/tempstuff

You then run 'rm -rf .'

You just deleted your entire file system.

What happens if another process deletes your working directory?
What directory are you in now?

By always using relative or absolute paths you know you are operating on the correct directory.

Yes, lots of people use cd/pushd/popd and yes, they work as expected.

The problem is that they are risky and best practice says to stay away from them.

Personally I dislike Bash to the extent that I've written a tool to replace it. If you are interested in using a modern programming language to write scripts have a look at:

pub.dev/packages/dshell

 

Thanks for the post :)

Minor copy&paste in the section explaining the use of copy the first example uses the command 'mv' instead of 'cp'.

 

Ha! Thanks for pointing it out! Fixed!