A script designed to organize the most important information about shell programming. I did it as a cheat sheet.
It was not my intention that this document be complete.
Pull requests are welcome!
- Write one :)
- Set appropriate permissions (everyone can execute 755, only owner 700)
- Put script in a known location (defined in PATH) or start execution as
./<script_name>
#!/bin/bashis called shebang, this way we can tell the OS which interpreter to choose Should be placed at the beginning of the file- Full flags are better, like
ls --all(not abbreviationls -a) - Use indentation and multiline commands using
\(improve readability) - Set in .vimrc
:syntax on,:set tabstop=4,:set number,:set autoindent - Way to enforce the immutability =>
declare -r NAME="ADAM"(the shell prevent any redefinition) - Shell treats every variable as string by default
- If aliases limited you, you can create a function inside the file .bashrc
- Quoted variable - defense againt the param being empty which cause error and may lead to unexpected results
- Use
>&2to send error messages to stderr
- By adding an extra argument
-xto shebang, tracing can be set - It's also possible by adding
set -xbefore section you want to debug andset +xto disable it
- Uppercase if constant
- Accepts letters, numbers, underscores
NAME="Adam"
a="My name is $NAME"
b="My name is ${NAME}-Szustak" # assigns variable and concatenate
c=$(ls -a) # assigns result of command
d=$((1 * 3)) # assigns math result
NAME="Borys" echo "second" # variable assignment is valid only in that line
echo $NAME # gives `Adam`
- Used to read a single line of stdin, can be used to read keyboard input, or when redirection is used, a line of data from a file
- If no variables are given, it assigns to default variable called REPLY
-ttimedout,-ssecret,-pprompt
read -p "Enter your name: " NAME
read -t 10 -sp "Enter password -> " PASSWD
echo $NAME $PASSWD
- Each command issues a value to the system when terminate (zero -> ok, non-zero -> error)
echo $?checks exit status
true
echo $? # gives `0`
false
echo $? # gives `1`
| Command | Means |
|---|---|
| nr1 -eq nr2 | returns true if the values are equal |
| nr1 -ne nr2 | returns true if the values are not equal |
| nr1 -lt nr2 | returns true if nr1 is less than nr2 |
| nr1 -le nr2 | returns true if nr1 is less than or equal to nr2 |
| nr1 -gt nr2 | returns true if nr1 is greater than nr2 |
| nr1 -ge nr2 | returns true if nr1 is greater than or equal to nr2 |
NR1=22
NR2=44
if [[ "$NUM1" -gt "$NUM2" ]]; then
echo "$NR1 is greater than $NR2"
else
echo "$NR1 is less than or equal $NR2"
fi
| Command | Means |
|---|---|
| -n string | returns true if len(string) is non-zero |
| -z string | returns true if len(string) is zero |
| string =~ regex | returns true if string match regex |
| string1 == string2 | returns true if strings are equal |
| string1 != string2 | returns true if strings are not equal |
AMOUNT=15USD
if [[ "$AMOUNT" == 15USD ]]; then
echo "It costs $AMOUNT"
elif [[ "$AMOUNT" =~ ^[0-9]+[A-Z]+$ ]]; then
echo "It costs another amount $AMOUNT"
elif ! [[ "$AMOUNT" =~ ^[0-9]+[A-Z]+$ ]]; then
echo "perhaps $AMOUNT is not correct"
fi
- AND ->
&& - OR ->
|| - NOT ->
!
VALUE=33
if [[ "$VALUE" -ge "$NR1" && "$VALUE" -le "$NR2" ]]; then
echo "Value is located between"
fi
| Command | Means |
|---|---|
| file1 -ef file2 | returns true if files have the same inode nr |
| file1 (-nt | -ot) file2 |
| -d file | returns true if the file is a directory |
| -e file | returns true if the file exists |
| -f file | returns true if the provided string is a file |
| -g file | returns true if the group id is set on a file |
| -s file | returns true if the file has a non-zero size |
| -u file | returns true if the user id is set on a file |
| -r file | returns true if the file is readable |
| -w file | returns true if the file is writable |
| -x file | returns true if the file is an executable |
FILE=test.txt
if [[ -e "$FILE" ]]; then
if [[ -d "$FILE" ]]; then
echo "$FILE is dir"
elif [[ -f "$FILE" ]]; then
echo "$FILE is file"
else
echo "$FILE N/A" >&2
exit 1
fi
else
echo "$FILE does NOT exist" >&2
exit 1
fi
- An additional form of I/O redirection
cmd <<- token- command that accepts stdin (such as cat, ftp) and a token in a string to indicate the end of the embedded text
cat <<- _EOF_
1 line
2 line
_EOF_
- cmd <<< line
read user <<< $(ls)
echo $user
- As long as exit status is 0, it executes commands inside the loop
- Break/continue, accepts stdin
count=0
while [[ $count -le 3 ]]; do
echo $count
count=$((count+1))
done
ls | while read file; do # piping to while
echo $file
done
- Multiple-choice command (e.g. to avoid multiple IFs)
- Accepts regex like
[ABC][C-9],*.txtor???) - Only matches one case, if multiple selections -> use
;;&instead of;;
case $NAME in
q|Q) echo "Exiting"
;;
[[:alpha:]]+) echo "OK"
;;
*) echo "Invalid
;;
esac
function sayHello() {
echo "Hello World"
}
# **** 2nd option ****
sayHello () {
local foo=bar # local var for func
echo "Hello World"
}
sayHello # call a func
- Instead of using IFs, you can use 1-line checking
read -p "Input item > "
[[ -z $REPLY ]] && sayHello # if var is empty then trigger function
- Variables from
$0contains the given arguments,${99} $0always contains path to the script$#contains nr of given args
$ ./script.sh 1a 2b 3c
echo $0 $1 $2 # prints `/home/me/.../script.sh 1a 2b`
echo "Accepted $# args" # prints `Accepted 3 args`
greet() {
echo "I am $1 and I am $2"
}
greet "Adam" "29"
- Causes all the params to move down one each time it's executed (except
$0) $(basename $0)extracts script name from the path stored in$0
count=1
while [[ &# -gt 0 ]]; do
echo "Arg $count = $1" # prints each given argument
count=$((count + 1))
shift
done
for i in {A..D}; do
echo $i
done
- Substitute value if unset (only once, when expanding)
$ echo ${foo:-"bar"} # prints `bar`
$ echo $foo # empty value
- Default value if unset
$ echo ${foo:="bar"} # prints `bar`
$ echo $foo # prints `bar`
$ echo ${foo:="fooooo"} # prints `bar`
- If empty or unset, causes the script to exit with an error
$ foo=
$ echo ${foo:?"is empty"} # bash: foo: "is empty"
- Substitute value if set
$ foo=
$ echo ${foo:+"bar"} # empty value
$ foo=foooooo
$ echo ${foo:+"bar"} # prints `bar`
- Get the given string lentgh
${#parameter}
$ foo="This is something"
$ echo ${#foo}
- Get part of the string
${parameter:offset:length}
$ foo="This is something"
$ echo ${foo:5:15} # prints `is something`
- Get portion of the string
${parameter##pattern}/${parameter#pattern}from THE END
$ foo=file.zip.txt
$ echo ${foo#*.} # prints `zip.txt`
$ echo ${foo##*.} # prints `txt`
- Get portion of the string
${parameter%%pattern}/${parameter%pattern}from THE BEGINNING
$ foo=file.zip.txt
$ echo ${foo%.*} # prints `file.zip`
$ echo ${foo%%.*} # prints `file`
- Replace part of the string
${parameter/pattern/string}/${parameter//pattern/string}
$ foo=JPG.JGP
$ echo ${foo/JPG/jpg} # prints `jpg.JPG`
$ echo ${foo//JPG/jpg} # prints `jpg.jpg`
- Change to upper/lowercase
${paramener,,}/${paramener^^}/${paramener^}
$ foo=aBcD
$ echo ${foo,,} # prints `abcd`
$ echo ${foo^^} # prints `ABCD`
$ echo ${foo^} # prints `Abcd`
- Base arithtmetic operations
$(( ))
$ echo $((5 - 6))
- Condition like
[[ $INT –eq 0 ]]is the same as((INT == 0))
if (( $INT == 0 )); then
echo null
fi
- Change number bases
$ echo $((51)) # base 10
$ echo $((8#51)) # octal
$ echo $((16#51)) # hexadecimal
$ echo $((2#1111)) # binary
if ((foo=5))gives true (makes foo equal 5 and evaluates true beacuse foo was assigned non-zero value)- Available operators:
parameter=value,parameter+=value,parameter-=value,parameter/=value,parameter*=value,parameter%=value,parameter++,parameter--,++parameter,--parameter, Difference between++parameterandparameter++- both increment by one, if at the front, the param is incremented before the param is returned.
for ((i = 0; i <= 20; i++)); do
if (((( i % 5)) == 0 )); then
printf "<%d> " $i
else
printf "%d " $i
fi
done
- If needed to perform higher math or just use floating point numbers
bcorawkprograms can be used
- Limited in bash to a single dimension
- Created automatically when they are accessed
$ a[1]=foo # assignment
$ echo ${a[1]} # access
- Assigning many values
$ days=(Mon Tue Wed Thu Fri)
$ echo $days # prints `Mon Tue Wed Thu Fri`
- Append new values and return len(array)
$ days+=(Sat)
$ echo ${#days[@]} # prints `6`
- Allows to combine the results of several cmds into a single stream
{ ls -l; echo "Listing; cat foo.txt } > output.txt
- When an event like logout or shutdown occurs, define what script will do
exit_on_signal_SIGINT () {
echo "Script interrupted" 2>&1
exit 0
}
trap exit_on_signal_SIGINT SIGINT
- Launch one or more child scripts that perform an additional task while the parent continues to run
async-child $ # to run script `async-child` in background
pid=$! # to get the process id of async-child
wait $pid # to stop execution of parent process till child is finished