\$\begingroup\$
\$\endgroup\$
My bash script mounts two VeraCrypt volumes, rsyncs their contents, and dismounts them. The script takes two parameters, the path for both the source and backup veracrypt volumes. I wrote this script so rsync wouldn't have to copy an entire volume if there was a single change in it.
This is my second time with bash scripting so please let me know what I could do better.
Thanks
#!/bin/bash
# Requirements:
# - Must have both veracrypt and rsync installed
# - Must be ran on Unix based systems
# - Both volumes must have the same password
function prepare_volumes_for_exit() {
# dismount volumes
veracrypt -d
echo "Volumes dismounted"
# delete mount points
sudo rm -r "$source_mount_point"
sudo rm -r "$backup_mount_point"
echo "Mount points purged"
}
# removes double or single quotes from strings of they exist
function format_string() {
if [ "${1:0:1}" == "'" ] && [ "${1: -1}" == "'" ]; then
echo "${1:1:-1}"
elif [ "${1:0:1}" == '"' ] && [ "${1: -1}" == '"' ]; then
echo "${1:1:-1}"
else
echo "1ドル"
fi
}
# make sure all volumes are removed before starting
veracrypt -d
# check that 2 params are passed
if ! [ $# -eq 2 ]; then
echo "Error: 2 parameters are required [source_volume_path] [backup_volume_path]"
exit 1
fi
echo "Enter the password for the veracrypt volume: "
read -s veracrypt_password
# path to veracrypt volumes
source_volume_path="$(format_string "1ドル")"
backup_volume_path="$(format_string "2ドル")"
# generate random mount point
source_mount_point="$(format_string "/mnt/$(uuidgen)")"
backup_mount_point="$(format_string "/mnt/$(uuidgen)")"
# flag to determine if drives are mounted
source_volume_mounted=0
backup_volume_mounted=0
# create mount points
sudo mkdir -p "$source_mount_point"
sudo mkdir -p "$backup_mount_point"
echo "Beginning to mount volumes"
sudo veracrypt -t --mount "$source_volume_path" "$source_mount_point" -p "$veracrypt_password" --non-interactive
# checks that the source volume mounted
while read -r drive_number volume_location vc_mapper mount_location; do
formatted_volume_location="$(format_string "$volume_location")"
formatted_mount_location="$(format_string "$mount_location")"
if [ "$formatted_volume_location" == "$source_volume_path" ] && [ "$formatted_mount_location" == "$source_mount_point" ]; then
echo "Source volume mounted"
source_volume_mounted=1
else
echo "Error: source volume not mounted"
prepare_volumes_for_exit
exit 1
fi
done <<< $(veracrypt -t -l)
sudo veracrypt -t --mount "$backup_volume_path" "$backup_mount_point" -p "$veracrypt_password" --non-interactive
# checks both the source and backup volumes are mounted
# http://mywiki.wooledge.org/BashFAQ/001#source
while read -r drive_number volume_location vc_mapper mount_location; do
formatted_volume_location="$(format_string "$volume_location")"
formatted_mount_location="$(format_string "$mount_location")"
if [ "$formatted_volume_location" == "$source_volume_path" ] && [ "$formatted_mount_location" == "$source_mount_point" ]; then
source_volume_mounted=1
elif [ "$formatted_volume_location" == "$backup_volume_path" ] && [ "$formatted_mount_location" == "$backup_mount_point" ]; then
echo "Backup volume mounted"
backup_volume_mounted=1
else
echo "Error: source or backup volume not mounted"
prepare_volumes_for_exit
exit 1
fi
done <<< $(veracrypt -t -l)
if [ "$source_volume_mounted" -eq 1 ] && [ "$backup_volume_mounted" -eq 1 ]
then
echo "Volumes mounted, beginning rsync"
# sudo rsync -hrtq --exclude-from='/media/miller/Primary/exclude.txt' --delete $source_mount_point/ $backup_mount_point
echo "rsync complete"
else
echo 'Error: Drives were not mounted successfully, skipping rsync'
fi
prepare_volumes_for_exit
echo "Complete"
exit 0
```
1 Answer 1
\$\begingroup\$
\$\endgroup\$
Some suggestions:
#!/usr/bin/env bash
is a more portable shebang line.function prepare_volumes_for_exit()
can be simplified toprepare_volumes_for_exit()
.- Using
sudo
within a script is generally discouraged, since it makes it easy to do really bad things without explicit user consent. Instead, remove thesudo
s and tell the user to run the whole script as root. format_string
is an anti-pattern. First, because quotes are actually valid characters in paths, but more importantly because it is a strong indication that you're expecting the user to wrongly input broken strings.! [ $# -eq 2 ]
can be simplified as[ $# -ne 2 ]
.-
can be improved aswhile read -r drive_number volume_location vc_mapper mount_location ... done <<< $(veracrypt -t -l)
This way, no word splitting happens on the input, and commands within thewhile read -r -u9 drive_number volume_location vc_mapper mount_location ... done 9< <(veracrypt -t -l)
while
loop which happen to read from stdin won't break the loop. exit 0
is redundant.- By convention, scripts should be silent if there is nothing important to report, and any error messages should go to standard error rather than standard output.
lang-bash