see also
zsh
bash
resources
https://shellhaters.org/
https://github.com/dylanaraps/pure-sh-bible
arguments
$0 - name of command itself
$1 - first argument
$# - count of arguments
default variable assignment
If variable not set or null, use default. If VARIABLE was unset or null, it still is after this (no assignment done).
FOO="${VARIABLE:-default}"
If variable not set or null, set it to default.
FOO="${VARIABLE:=default}"
if statements
if [ $1 -gt 100 ]
then
echo "big"
else
echo "small"
fi
Learn about the different comparisons with man test.
basic looping
for loops iterate through a set of values until the list is exhausted:
for i in 1 2 3 4 5
do
echo "$i"
done
while loops run a block of code while a condition is true:
count=0
while [ "$count" != 100 ]
do
echo $count
count=$((count+1))
done
loop over files and directories
# Greedy example.
for file in *; do
[ -e "$file" ] || [ -L "$file" ] || continue
printf '%s\n' "$file"
done
# PNG files in dir.
for file in ~/Pictures/*.png; do
[ -f "$file" ] || continue
printf '%s\n' "$file"
done
# Iterate over directories.
for dir in ~/Downloads/*/; do
[ -d "$dir" ] || continue
printf '%s\n' "$dir"
done
replace symlinks with the files they link to
unsym is a good name
#!/bin/sh
set -e
for link; do
test -h "$link" || continue
dir=$(dirname "$link")
reltarget=$(readlink "$link")
case $reltarget in
/*) abstarget=$reltarget;;
*) abstarget=$dir/$reltarget;;
esac
rm -fv "$link"
cp -afv "$abstarget" "$link" || {
# on failure, restore the symlink
rm -rfv "$link"
ln -sfv "$reltarget" "$link"
}
done
redirecting to /dev/null
Don't do it lol: https://torresjrjr.com/archive/2021-05-03-stop-using-dev-null/
# Silence the stdout output (fd 1) of ping (only print errors)
ping fediverse.party >&-
# Silence all output of curl
curl -v https://fediverse.party >&- 2>&-
idempotent scripts
Idempotent scripts can be called multiple times and each time it's called, it
will have the same effects on the system. This means, a second call will exit
with the same result and won’t have any side effects. For example, mkdir is
non-idempotent by default. If called twice it will error the second time. The
-p flag suppresses this error, fixing mkdir.
creating an empty file
Touch is by default idempotent.
touch example.txt
creating a directory
mkdir -p mydir
creating a symbolic link
ln -sf source target
removing a file
rm -f example.txt
modifying a file
Sometimes you're adding a new line to an existing file (i.e: /etc/fstab). This
means, you need to make sure not to add it the second time if you run your
script. One way of making this idempotent is to make sure to check for certain
placeholders via grep:
if ! grep -qF "/mnt/dev" /etc/fstab; then
echo "/dev/sda1 /mnt/dev ext4 defaults 0 0" | sudo tee -a /etc/fstab
fi
Here the -q means silent mode and -F enables fixed string mode. Grep will
silently fail if /mnt/dev doesn't exist so the echo statement will never be
called.
formatting a device
blkid "$VOLUME_NAME" || mkfs.ext4 "$VOLUME_NAME"
This command prints attributes for a given block device. Hence prepending
basically means to proceed with formatting only when blkid fails, which is an
indication that the given volume is not formatted yet.
mounting a device
The mountpoint command checks whether a file or directory is a mount point.
The -q flag just makes sure it doesn't output anything and silently exits. In
this case, if the mount point doesn't exist, it’ll go forward and mount the
volume.
if ! mountpoint -q "$DATA_DIR"; then
mount -o discard,defaults,noatime "$VOLUME_NAME" "$DATA_DIR"
fi
strip trailing /
When getting a path from an argument you often want to strip off the last / if
it exists.
REPO_DIR=${1%%"${1##*[!/]}"}