#!/usr/bin/env bash
Manually handle the error
chmod +x /root/FILE.sh
if [ $? -ne 0 ]; then
echo "[ERROR] chmod failed"
exit 1
fi
# with a function
function handleError {
if [ $1 -ne 0 ]; then
echo;
echo " [ERROR] $2"
echo;
exit $1
fi
}
ls /bad/path
handleError $? "Failed to list path"
echo "next step"
Have the script exit automatically when an error occurs (this can have adverse effects in some cases)
set -e
chmod +x /root/FILE.sh
Var | Description |
---|---|
$RANDOM |
It gives you a more or less random integer between 0 and 32767. |
Type | Operator | Action | Example |
---|---|---|---|
stdout Standard Output |
1> |
Overwrite | ls -la ~/ 1> "./out.log" |
stderr Standard Error |
2> |
Overwrite | cat /etc/sudoers 2> "err.log" |
stdout and stderr |
&> |
Overwrite | ls -la ~/ &> "./both.log"or cat /etc/sudoers &> "error.log" |
stdout |
1>> |
Append | ls -la ~/ 1>> "./out.log" && ls -la ~/ 1>> "./out.log" |
stderr |
2>> |
Append | cat /etc/sudoers 2>> "err.log" && cat /etc/sudoers 2>> "err.log" |
stdout and stderr |
&>> |
Append | ls -la ~/ &>> "./both.log" && cat /etc/sudoers &>> "both.log" |
Type | Definition | Description |
---|---|---|
$' |
ANSI C Quoting | Words of the form $'string' are treated specially. The word expands to string, with backslash-escaped characters replaced as specified by the ANSI C standard. |
$" |
Locale translation | A double-quoted string preceded by a dollar sign (‘$’) will cause the string to be translated according to the current locale. If the current locale is C or POSIX, the dollar sign is ignored. If the string is translated and replaced, the replacement is double-quoted. |
For integers
# Subtraction
expr 1 - 1
# Addition
expr 1 + 1
expr $myvar + 1
# Division
expr $myvar / 3
# Multiplication
expr $myvar \* 3
# via vars
myvar=6
let myvar+=1
let myvar+1
let myvar2=myvar+1
# declare and evaluate multiple items at once
let x=4 y=5 z=x*y u=z/2
Arithmetic Expansion
The
$((...))
notation is what is called the Arithmetic Expansion while the((...))
notation is called a compound command used to evaluate an arithmetic expression in Bash.The Arithmetic Expansion notation should be the preferred way unless doing an arithmetic evaluation in a Bash if statement, in a Bash for loop, or similar statements.
# one-off
echo $((2 + 2))
# with a var
sum=$((2 + 2))
((sum+=2))
((sum++))
# evaluate multiple items (commas required)
echo $((x=4, y=5, z=x*y, u=z/2))
For floats (just use awk
)
# one-off
awk 'BEGIN { print 100/3 }'
# pipes
echo '100' | awk '{ print $1/3 }'
# round up
echo '1.5' | awk '{ printf("%.0f\n", $1) }'
Floating point rounding in GNU awk may not always behave as expected. For example, both
1.5
and2.5
round to2
, since the FP system rounds halves to the nearest even number, not up.
# directory exists
if [ -d "<PATH>" ]; then
# logic
fi
# directory doesn't exist
if [ ! -d "<PATH>" ]; then
# logic
fi
# file exists
if [ -f "<PATH>" ]; then
# logic
fi
# file doesn't exist
if [ ! -f "<PATH>" ]; then
# logic
fi
If you have permissions for the file
# overwrite file
echo "text" > "<FILE>"
# append to file
echo "text" >> "<FILE>"
If it requires sudo
# overwrite file
echo "text" | sudo tee "<FILE>" > /dev/null
# append to file
echo "text" | sudo tee -a "<FILE>" > /dev/null
# make file executable
chmod +x "<FILE>"
# make read/writable by all
chmod a+rw "<FILE>"
# make read/writable by group
chmod g+rw "<FILE>"
# make read/writable by user (`u` not neccessary since it's the default, just calling out for completeness)
chmod u+rw "<FILE>"
ENABLE_X=false
remainingArgs=()
while [[ $# -gt 0 ]]; do
case $1 in
-a1|--arg1) # arg with value
VAR1="$2"
shift 2
;;
-a2|--arg2) # arg as a flag
ENABLE_X=true
shift
;;
-*|--*)
echo "Unknown option $1"
exit 1
;;
*)
remainingArgs+=("$1")
shift
;;
esac
done
set -- "${remainingArgs[@]}"
envVars=("VAR1=val1" "VAR2=val2")
# construct vars for 'docker run' (notice the use of --, which is needed to tell printf not to interpret the next items as commandline options since the format has '-e' which could be interpreted as a flag)
args=$(printf -- "-e %s " "${baseEnvVars[@]}")
# construct vars for 'docker-compose' (-- not needed for this format)
args=$(printf "export %s; " "${baseEnvVars[@]}")
Use to output multiline formatted file
cat << EOF > file.txt
The current working directory is: $PWD
You are logged in as: $(whoami)
EOF
if true; then
cat <<- EOF
Line with a leading tab.
EOF
fi
- https://www.cyberciti.biz/tips/bash-shell-parameter-substitution-2.html
- https://www.gnu.org/software/bash/manual/html_node/Shell-Parameter-Expansion.html
${parameter:-defaultValue}
: Default value for variable
VAR=${parameter:-"some value"}
${parameter:=defaultValue}
: Assign default value if one doesn't exist
${USER:=nobody}
${parameter:?"error message"}
: Exit with error message if parameter not set
${1:?"ERROR: You didn't provide a required argument"}
${#parameter}
: Get length of parameter
[[ ${#var} -ge 9 ]] && { echo "var too long"; exit 1; }
${var#Pattern}
: Remove pattern from front
URL="http://domain.com/file.tar.gz"
echo "${URL#*/}"
# result: /domain.com/file.tar.gz
${var##Pattern}
: Remove last occurance of pattern, from front
URL="http://domain.com/file.tar.gz"
echo "${URL##*/}"
# result: file.tar.gz
${var%Pattern}
: Remove pattern from end
FILE="file.tar.gz"
echo "${FILE%.*}"
# result: file.tar
${var%%Pattern}
: Remove last occurance of pattern, from end
FILE="file.tar.gz"
echo "${FILE%%.*}"
# result: file
${parameter/pattern/string}
: Replace pattern with String
FILENAME="example.tbn"
echo "${FILENAME/.tbn/-thumb.jpg}"
# result: example-thumb.jpg
${parameter//pattern/string}
: Replace all occurances of pattern with String
FILENAME="/some/old/path/with/old/file.jpg"
echo "${FILENAME//old/new}"
# result: /some/new/path/with/new/file.jpg
${parameter:offset:length}
: Extract section of parameter
URL="http://domain.com/path"
echo "${parameter:7:10}"
# result: domain.com
${!pattern}
: Get all variables starting with pattern
VAR_1="One"
VAR_2="Two"
VAR_3="Three"
echo "${!VAR_*}"
${var^}
: Capitalize variable
VAR="name"
echo "${VAR^}"
result: Name
${var^^}
: Uppercase variable
VAR="name"
echo "${VAR^^}"
result: NAME
${var,}
: Lowercase first letter in variable
VAR="NAME"
echo "${VAR,}"
result: nAME
${var,,}
: Lowercase variable
VAR="NAME"
echo "${VAR,,}"
result: name
Replacing a token with a variable that contains slashes. Just switch from the /
delimeter to another character.
echo '"${PWD}/script.sh" -i "${PWD}/temp.log"' | sed "s|\${PWD}|${PWD}|g"
Un/comment a line
# uncomment
sed "/^#zstyle ':omz:update' mode disabled/s/^#//" ~/.zshrc
# comment
sed "/^zstyle ':omz:update' mode disabled/s/^/#/" ~/.zshrc
Replace multiple lines
- start token:
/plugins=(/
- end token:
/)/
sed "/plugins=(/,/)/c\plugins=(\n NEW_VALUE\n)" ~/.zshrc
List folders and files as a list. Folders first, then files.
# A: all files execpt . and ..
# d: only directory names, not their contents
# 1: one item per line
# p: add a trailing slash to folders
ls -Ad1p --group-directories-first <FOLDER_PATH>
If binary doesn't exist, install it
if ! command -v BINARY_NAME &> /dev/null; then
# install
fi
Insert text at the top of a file
text='# comment\n'
text+='FU=bar\n\n'
sed -i '1 i\'"${text}" "${HOME}/.zshrc"
Run logic if text not in file
if ! grep -q "$user2" /etc/passwd; then
echo "User does not exist!!"
fi
Get currently running script path and name
SCRIPT_PATH="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &> /dev/null && pwd)"
SCRIPT_NAME=$(basename "$0")
echo "${SCRIPT_PATH}/${SCRIPT_NAME}"
Parse columns of data. They key bit being awk -v col=2 '{print $col}'
.
echo 'col1 col2 col3 col4' | awk -v col=2 '{print $col}'
# output: col2
echo 'col1 col2 col3 col4' | awk -v col2=2 -v col4=4 '{print $col2, $col4}'
# output: col2 col4
# Note the comma is neccessary for spacing
# Specify an alternate seperator with '-F'. Note that a multi-character separator may only work in certain versions of awk.
# A multi-character separator would be `-F '[-_]'`, just like a regex.
echo 'col1|col2|col3|col4' | awk -F '|' -v col=2 '{print $col}'
# output: col2
List folders that don't contain a specific file (in this case thumb.jpg
)
find . -mindepth 1 -maxdepth 1 -type d '!' -exec test -e "{}/thumb.jpg" ';' -print | sort
Remove files based on part of it's name. -print0
is used to print out each file on the same line (as an argument), and xargs -0
is used to pass each item from the pipe to rm
. The -0
is to handle possible spaces in an arg.
find ./src/ -name "*.test.js" -print0 | xargs -0 rm
Get a list of files withing a directory based on their extension.
find "<DIRECTORY>" -type f \( -name "*.mp4" -o -name "*.mkv" -o -name "*.avi" \)
Handle multiple folders or files within specific directories. You can use multiple nested braces to manipulate any folder or file you want.
# copy a folder and file to another directory
cp -rp ./parent/child/{folder,file.json} ../another_parent/child/
# move folders and files to another directory
mv ./parent/child/{folder/{nested1,nested2},file.json} ../another_parent/child/
Print the full path of a file
find "${PWD}/<FILE>"
Print the name of a file without it's extension
echo "$(basename "<FILE>")" | { read n; echo "${n%.*}"; }
Prompt for a response. Note that ZSH uses the format read "<VAR>?<QUERY>"
, where as Bash sometimes uses read -r "<QUERY>" <VAR>
.
##
# store previous session data
onExit() {
if [[ "${PWD}" != "${HOME}" ]]; then
echo "export CUSTOM__PREV_DIR='${PWD}'" > ~/.prev_term
fi
}
trap onExit EXIT
if [ -f ~/.prev_term ]; then
source ~/.prev_term
# only prompt to load previous dir if one has been saved, and a new shell was started
if [[ "${CUSTOM__PREV_DIR}" != "" ]] && [[ "${CUSTOM__PREV_DIR}" != "${HOME}" ]] && [[ "${PWD}" == "${HOME}" ]]; then
while true; do
read "yn?Load '${CUSTOM__PREV_DIR}' (y/n)?: "
case $yn in
[Yy]* )
cd "${CUSTOM__PREV_DIR}"
break
;;
[Nn]* ) break;;
* ) echo "Please answer yes or no.";;
esac
done
fi
fi