1
\$\begingroup\$

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
```
asked May 2, 2021 at 21:27
\$\endgroup\$

1 Answer 1

1
\$\begingroup\$

Some suggestions:

  • #!/usr/bin/env bash is a more portable shebang line.
  • function prepare_volumes_for_exit() can be simplified to prepare_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 the sudos 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 ].
  • while read -r drive_number volume_location vc_mapper mount_location
    ...
    done <<< $(veracrypt -t -l)
    
    can be improved as
    while read -r -u9 drive_number volume_location vc_mapper mount_location
    ...
    done 9< <(veracrypt -t -l)
    
    This way, no word splitting happens on the input, and commands within the 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.
answered May 3, 2021 at 1:16
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.