Bash scripting
Google recommends using bash
for all executable shell scripts:
Restricting all executable shell scripts to
bash
gives us a consistent shell language that's installed on all our machines. In particular, this means there is generally no need to strive for POSIX-compatibility or otherwise avoid "bashisms".
For practical examples and command-line usage, see the CLI guide.
Shebang
The shebang is the first line of a script and tells the system which interpreter
to use to run the script. For bash
scripts, use #!/bin/bash
:
#!/bin/bash
# After the shebang, add comments to describe the script
echo "Hello, world!"
Executing scripts
To execute a script from the command line, make it executable:
chmod +x /path/to/script.sh
Note
If you use the command ls -l
, you'll see an x
in the permissions column
for the script. More about that here.
Then run it:
./path/to/script.sh
Tip
The ./
prefix is required to run scripts in the current directory.
Try saving the "Hello, world!" script above to a file and running it
after making it executable. (The .sh
extension is optional.)
Resources
man bash
- Bash Reference Manual
- Bash Guide
- Advanced Bash-Scripting Guide
- Safety First!
- Bashisms
- Bash Pitfalls
- Arrays in Bash
- Shell Script Templates
- Command Name Extensions Considered Harmful
For a quick reference, check out DevHints.io.
Special Variables and Commands
Special variables and commands can be very useful in bash
scripts, particularly for interacting with command history:
Command | Description |
---|---|
!! |
Repeat the last command. |
!$ |
Last argument of the last command. |
$_ |
Last argument of the last command. |
!^ |
First argument of the last command. |
$? |
Exit status of the last command. |
!n |
Repeat command number n from history. |
!!:n |
The n -th argument of the last command. |
!!:s/old/new/ |
Substitute old with new in the last command. |
!cmd |
Run the most recent command starting with cmd . |
!+ |
Next command in the history list (after the current one). |
!-n |
The command n lines before the current command in history. |
!!:p |
Print the last command without executing it. |
!!:x |
Extract and display the x -th argument of the last command. |
Examples
You ran a command without sudo
and want to run it again with sudo
:
$ apt update
E: Could not open lock file /var/lib/apt/lists/lock - open (13: Permission denied)
$ sudo !!
You performed a dry run with echo
and want to run the command for real:
$ echo sudo apt-get install -y vim
$ !:2-*
Update and Upgrade Everything on Ubuntu
To update and upgrade all packages:
sudo apt-get update && sudo apt-get upgrade -y
Alternatively:
yes | sudo sh -c 'apt update && apt upgrade && apt dist-upgrade \
&& apt autoremove && apt autoclean && apt clean'
Special text inside bash
scripts
Multiline Comments
To add multiline comments in bash
, use the following syntax:
: '
This is a
very neat comment
in bash
'
Here Documents
A here document allows you to pass multiple lines of input to a command without storing it in a file:
cat << EOF
This is a here document.
It can be used to pass multiple lines of input to a command.
EOF
More useful commands
Command | Description |
---|---|
ssh user@host 'bash -s' < script.sh | Run a local script on a remote machine. |
md5sum ./*.fastq.gz > md5sums.txt | Generate a file with the MD5 checksums of all files in the current directory. |
md5sum -c md5sums.txt | Check files against the MD5 checksums in a file. |
du -ha --max-depth=1 | Check the total size of each folder in the current directory. |
free -h | Check current memory usage. |
Loop across files of the same extension
Don't use ls
or find
. Instead, use a glob pattern:
for file in ./*.bam; do # Loop across all .bam files in the current directory
command "$file" # Quote the variable to handle spaces in filenames
done
# single-line loop
for file in ./*.bam; do command "$file"; done
Safely change directories
Don't use cd
without checking if it was successful.
You might end up executing commands in the wrong directory.
cd /path/to/some/dir || exit # Exit if the directory doesn't exist or is inaccessible
rm -rf * # This could be very bad if the cd failed
Correctly define functions
Don't use function
to define functions! It's ugly and not portable.
Just use the function name followed by parentheses. We'll know it's a function.
# bad
function myfunc {
echo "Hello, world!"
}
# very bad
function myfunc() {
echo "Hello, world!"
}
# good
myfunc() {
echo "Hello, world!"
}
Tip
To define single-line functions, add a semicolon before the closing brace:
myfunc() { echo "Hello, world!"; }
Page last updated on 2024-10-17.