I'm fairly proficient in C and Python, but want to learn some skills for administrating my new Linux machine. I wrote this simple address book app to teach myself shell scripting. This is the first script I've written longer than a couple of lines.
The address book is a plain text file stored in $HOME/.sab-db
. Entries are separated by newlines and the three fields (first name, surname, email) are separated by colons, like the fields in /etc/passwd
.
The application is divided into four scripts that have a single task each. This is inspired by how Git is organized. Is this a good way to isolate independent code in bash?
How should I handle shared code between scripts? I've use two different approaches below: I created a standalone script that the others call for determining the database name, and I copy-pasted the die
function. I've heard of script sourcing, but I've never seen that used for creating bash "libraries".
sab-find-db
#!/bin/bash
DB_NAME=".sab-db"
if [ -n "$SAB_DB_DIR" ]; then
echo "$SAB_DB_DIR/$DB_NAME"
else
echo "$HOME/$DB_NAME"
fi
sab-search
#!/bin/bash
function die {
echo "$1" >&2
exit 1
}
if [ $# -eq 1 ]; then
# Fields can't include : since it's the delimiter
if [ $(echo "$1" | grep ":" | wc -l) -gt 0 ]; then
exit 0
else
file=$(mktemp)
grep -i "$1" $(./sab-find-db) >"$file" 2>/dev/null
fi
elif [ $# -eq 0 ]; then
file=$(./sab-find-db)
else
die "usage: $0 [<first name> | <surname> | <email>]"
fi
cat "$file"
while read line; do
echo "First name: $(echo "$line" | cut -d: -f1 -)"
echo "Surname: $(echo "$line" | cut -d: -f2 -)"
echo "Email: $(echo "$line" | cut -d: -f3 -)"
echo
done <"$file"
if [ $# -eq 1 ]; then
rm "$file"
fi
sab-add
#!/bin/bash
function die {
echo "$1" >&2
exit 1
}
if [ $# -eq 3 ]; then
entry="$1:$2:$3"
elif [ $# -eq 0 ]; then
echo -n "First name: "
read first_name
echo -n "Surname: "
read surname
echo -n "Email: "
read email
entry="$first_name:$surname:$email"
else
die "usage: $0\n [<first name> <surname> <email>]"
fi
if [ $(echo "$entry" | grep -o ":" | wc -l) -gt 2 ]; then
die "Fields can't include colon"
# Check if any : is next to another : or start or end of line
elif [ $(echo "$entry" | grep "\(^:\)\|\(::\)\|\(:$\)" | wc -l) -gt 0 ]; then
die "No fields can be left empty"
elif [ $(grep -i "^$entry\$" $(./sab-find-db) 2>/dev/null | wc -l) -gt 0 ]; then
die "This entry already exists. It was not added again."
else
echo "$entry" >>$(./sab-find-db)
fi
sab-remove
#!/bin/bash
function die {
echo "$1" >&2
exit 1
}
if [ $(grep -i "\<$1\>" $(./sab-find-db) 2>/dev/null | wc -l) -eq 0 ]; then
die "No match found"
fi
file=$(mktemp)
if [ ! $(grep -vi "\<$1\>" $(./sab-find-db) >"$file" 2>/dev/null) ]; then
mv "$file" $(./sab-find-db)
else
rm "$file"
fi