I created this Bash script as a project for the system administration course I’m taking for the summer. I’m sure there are bugs in it, so let me know if you find any.
It basically uses an XML configuration file, which includes the source, destination, and any excludes from the transfer. You then pass either -u or -d (upload or download) and the options -x (delete if not in source) and -f (force). Destination can be local or remote, but source must be local.
Here is the code:
#!/bin/bash
## Transfer Script
## By David Drager
## CSC586: Summer II 2008
## Requires: xml2, rsync
## Settings
tempdir="/tmp/"
rsynccommand="/usr/bin/rsync"
## Usage command
usage="usage: transfer.sh [options] .xmlnoptions:n -d or -u:t download or upload, resp. one and only one must be presentn -f:ttforce transfer regardless of 'newness' of filen -x:ttdelete items in target not present in source"
# Check to make sure temporary directory exists
[ -d $tempdir ] || { echo -e "Error: Could not locate a temporary directory. See file settings."; exit 1; }
# Usage Command
[ $# -eq 0 ] && { echo -e "ERROR: Needs at least the -d or -u flag plus a config file.nn$usage"; exit 1; }
## Get options passed to the program
while getopts "dufx" flag
do
[ "$flag" = "?" ] && echo -e "ERROR: Flag(s) not valid.nn$usage" && exit 1;
eval "opt_$flag=1"
done
shift $((OPTIND-1))
configfile="$1"
if [ "$opt_d" = 1 ] && [ "$opt_u" = 1 ]
then
echo -e "Error: Use either -u Upload or -d Download, but not bothn$usage"; exit 1;
elif [ "$opt_u" = "" ] && [ "$opt_d" = "" ]
then
echo -e "Error: Use either -u Upload or -d Download, but not bothn$usage"; exit 1;
fi
if [ "$opt_f" = 1 ]; then
force=1
fi
if [ "$opt_x" = 1 ]; then
deltar=1
fi
## Make sure config file is located
if [ "$configfile" != "" ]
then
[ -f "$configfile" ] || { echo -e "Error: Config file not found.n$usage"; exit 1; }
else
echo -e "Error: Config file not specified.n$usage"; exit 1;
fi
## End of swich verification
# echo Action: "$action"
# echo Force: "$force"
# echo Delete Target Files: "$deltar"
# echo Config: "$configfile"
## Now read config file
## Set temp file for XML parsing
tempfile="$tempdir"transferscript_$$
tempexcludes="$tempdir"transferscriptexcludes_$$
# echo -e "Temp:$tempfile"
# Run config file through xml2
xml2 < "$configfile" > "$tempfile"
[ -f "$tempfile" ] || { echo -e "There was a problem processing the XML file."; exit 1; }
# Make sure that source and destination are found
excludelist=""
while read line; do
key=$(echo $line | awk -F '=' '{print $1}')
value=$(echo $line | awk -F '=' '{print $2}')
if [ "$key" = "/sync/@src" ]; then
source="$value";
elif [ "$key" = "/sync/@dst" ]; then
destination="$value"
# Add any exclude options to array
elif [ "$key" = "/sync/exclude" ]; then
tempexcl=("$value")
excludelist=("${excludelist[@]}" "${tempexcl[@]}")
fi
done < "$tempfile"
[ "$source" ] || { echo "Error: Config file must include a source"; exit 1; }
[ "$destination" ] || { echo "Error: Config file must include a destination"; exit 1; }
source="$source/"
destination="$destination/"
[ -d "$source" ] || { echo "Error: Source must be a local directory"; exit 1; }
excludes=""
excludes=${excludelist[@]}
# echo Excludes: "(${#excludelist[@]}): $excludes"
## Clean up temp file from xml process
rm "$tempfile"
## Build up the command line
[ -f "$rsynccommand" ] || { echo -e "Error: Could not locate rsync command."; exit 1; }
fullcommand="$rsynccommand"
fulltestcommand="$rsynccommand"
if [ "$opt_f" == "1" ]
then
fulltestcommand="$fulltestcommand -nva"
fullcommand="$fullcommand -a"
else
fulltestcommand="$fulltestcommand -nvau"
fullcommand="$fullcommand -au"
fi
if [ "$opt_u" == "1" ]
then
fileorder="$source $destination"
echo "Evaluating transfer from $source ==> $destination"
elif [ "$opt_d" == "1" ]
then
fileorder="$destination $source"
echo "Evaluating transfer from $destination ==> $source"
fi
[ "$opt_x" == "1" ] && { fulltestcommand="$fulltestcommand --delete"; fullcommand="$fullcommand --delete"; }
## Move exclude list to a file.
## This duplicates as above but I originally thought we were passing it to rsync as a list and not as a file.
[ "${#excludelist[@]}" -gt 0 ] && {
tempfile="$tempdir"transferscript_$$
touch "$tempfile"
echo "$excludes" | tr " " "n" > "$tempfile"
fulltestcommand="$fulltestcommand"" --exclude-from=$tempfile"
fullcommand="$fullcommand"" --exclude-from=$tempfile"
}
fulltestcommand="$fulltestcommand $fileorder"
fullcommand="$fullcommand $fileorder"
# We only want lines minus top line and bottom 3 lines
testresult=`$fulltestcommand | tail --lines=+2 | head --lines=-3`
# If this is empty, then notify that no files would be transferred.
[ "$testresult" ] || { echo "This command would not transfer any files. Script exiting."; rm "$tempfile"; exit 1; }
# Display changes
echo -e "Changes to be made:n--------------------n$testresultn--------------------"
# Check to see if we really want to run the command.
echo "Do you wish to perform this command? $fullcommand (y/n)[y]"
read confirm
if [ "$confirm" = "y" ] || [ "$confirm" = "" ]
then
echo "Performing transfer..."
# Without -v this will not output anything.
$fullcommand
echo "Transfer complete."
rm "$tempfile"
else
echo "Cancelling transfer..."
rm "$tempfile"
fi
exit 0
Here is the sample config file:
.test3
another.*
1 comment
Comments are closed.