Last active
March 23, 2025 07:48
-
-
Save ljfranklin/99fc9af42aa732b33525 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
## Bash Prog Intro Notes | |
http://tldp.org/HOWTO/Bash-Prog-Intro-HOWTO.html | |
http://tldp.org/LDP/abs/html/ | |
http://mywiki.wooledge.org/BashPitfalls | |
## Intro: | |
I feel like writing bash is like building something with hot glue. | |
When try it for the first time, you'll probably make a big mess and burn yourself. | |
As you get a little more experienced, you can slap things together really quickly but the end result won't be very sturdy or pretty. | |
This guide provides a cheatsheet for bash functions that I use frequently. | |
The commands start fairly simply with a handful of more advanced commands and gotchas towards the end. | |
## Basic Commands: | |
Write stdout, stderr, both to file: | |
``` | |
$out_cmd > stdout.log | |
$err_cmd 2> stderr.log | |
$both_cmd &> combined.log | |
``` | |
Assign output of command to variable | |
``` | |
output="$(echo 'hello!')" | |
``` | |
Positional Parameters | |
``` | |
./my-script hello world | |
echo "$0" # ./test, script path | |
echo "$1" # hello, first positional arg | |
echo "$2" # world, second arg | |
echo "$#" # 2, total number of args | |
echo "$*" # "hello world", expands all args as a single word | |
echo "$@" # "hello" "world", expands all args as separate words | |
# shift | |
echo "$1" # hello | |
shift # discard first arg, slide remaining args to left | |
echo "$1" # world | |
``` | |
Parameter Substitution: | |
``` | |
ARG="${1:-default_value}" # `:-` returns LHS if not null, RHS otherwise | |
: ${ENV_VARIABLE:=default_value} # `:=` sets ENV_VARIABLE to `default_value` if ENV_VARIABLE is null. | |
# The leading `:` prevents the shell from trying to run the value. | |
: ${ENV_VARIABLE:?ENV_VARIABLE must be set} # exit immediately if ENV_VARIABLE is null, trailing error message is optional | |
``` | |
Integer Arithmetic | |
``` | |
echo "$((2+2))" # 4 | |
a=1 | |
(( a++ )) # sets `a` to 2 | |
echo "$(( a++ ))" # still prints 2, then sets value to 3. Be careful with pre vs post increment! | |
``` | |
If statements | |
``` | |
if [ $condition0 ]; then | |
command0 | |
elif [ $condition1 ]; then | |
command1 | |
else | |
command2 | |
fi | |
``` | |
For loops | |
``` | |
# print 1 through 10 inclusive | |
for i in $(seq 1 10); do # be sure not to quote "$(...)" | |
echo "$i" | |
done | |
# print each arg | |
for arg in "$@"; do | |
echo "$arg" | |
done | |
``` | |
Iterate over lines in a file | |
``` | |
while read line; do | |
echo "$line" | |
done < input.txt | |
``` | |
Case statements | |
``` | |
#!/bin/bash | |
case "$1" in | |
start) | |
echo "Starting..." | |
;; | |
stop | shutdown) | |
echo "Stopping..." | |
;; | |
*) | |
# default case | |
echo "ERROR: Unrecognized option '$1'" | |
echo "Usage: my_script {start|stop|shutdown}" | |
;; | |
esac | |
``` | |
Test Operators | |
``` | |
# integers | |
if [ "$a" -eq "$b" ] # equal | |
if [ "$a" -ne "$b" ] # not equal | |
if [ "$a" -gt "$b" ] # greater than | |
if [ "$a" -lt "$b" ] # less than | |
# strings | |
if [ "$a" == "$b" ] | |
if [ "$a" != "$b" ] | |
# variables | |
if [ -z "$1" ] # succeeds if $1 is unset | |
if [ -n "$1" ] # succeeds if $1 is set | |
# boolean operators | |
if [ ! $condition ] # invert result | |
if [ $condition0 ] && [ $condition1 ] # and | |
if [ $condition0 ] || [ $condition1 ] # or | |
## file operators | |
if [ -e $file ] # true if file or directory exists | |
if [ -f $file ] # true if file exists, false for directories | |
if [ -d $dir ] # true if directory exists, false for files | |
``` | |
Get the previous command's exit code: | |
``` | |
somd_cmd | |
echo "$?" | |
``` | |
Capture PID and write to file: | |
``` | |
# replace current process with another process | |
echo $$ > $pidfile | |
exec some_process | |
# write pid of background process | |
some_process & | |
echo $! > $pidfile | |
``` | |
## Text processing commands | |
Print the n-th item in a line | |
``` | |
WORDS="this is only a test" | |
echo "$WORDS" | cut -d ' ' -f3 # prints "only", index starts at 1 | |
echo "$WORDS" | cut -d ' ' -f1-3 # prints "this is only" | |
echo "$WORDS" | cut -d ' ' -f1,f5 # prints "this test" | |
CSV="first,last,address" | |
echo "$CSV" | cut -d ',' -f2 # prints "last", changes delimiter | |
``` | |
Replace changes in string | |
``` | |
CSV="first,last,address" | |
echo "$WORDS" | sed 's/,/\n/g' # prints "first\nlast\naddress" | |
``` | |
Get leading or trailing lines in file | |
``` | |
head -n1 ./some_file # prints first line in file | |
tail -n1 ./some_file # prints last line in file | |
tail -f ./process.log # streams file contents as new lines are added, useful for debugging | |
``` | |
Extract file or directory names | |
``` | |
FILE_PATH=/home/foo/my-file.txt | |
FILE="$(basename $FILE_PATH)" # "my-file.txt" | |
DIR="$(dirname $FILE_PATH)" # "/home/foo" | |
``` | |
Search with regex | |
``` | |
# print lines matching regex | |
grep 'value' ./some-file | |
echo "$VAR" | grep 'value' # can be used with pipes | |
grep -i 'vAluE' ./some-file # case insensitive | |
grep -v 'value' ./some-file # inverse, print lines not matching regex | |
# check for running process name | |
ps ax | grep mysql | |
``` | |
## Advanced commands | |
Regex | |
Notes: | |
- Uses POSIX regex, e.g. `[[:digit:]]` instead of `\d` | |
- To avoid unexpected behavior, always store regex in a variable and | |
do not quote the variable after the `=~` operator. | |
- Regex is tough to read at the best of times, bash syntax makes it harder. | |
Please use sparingly. | |
``` | |
REGEX='[[:digit:]]{1,3}\.[[:digit:]]{1,3}\.[[:digit:]]{1,3}\.[[:digit:]]{1,3}' | |
if [[ "10.10.0.255" =~ $REGEX ]]; then # do not surround regex variable in quotes | |
echo "Match!" | |
else | |
echo "No match..." | |
fi | |
# capture groups | |
REGEX='[a-z]+_([a-z]+)' # capture letters following `_` | |
[[ "first_last" =~ $REGEX ]] | |
last_name="${BASH_REMATCH[1]}" # [0] is the full match, [1] is the first capture group | |
``` |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment