From 6c7eea240133c6a3af6cddc0216b01cddd009855 Mon Sep 17 00:00:00 2001 From: zertrin Date: Sat, 3 Sep 2016 23:18:46 +0200 Subject: [PATCH 1/2] Big rework of logging handling. Add a --quiet option to suppress most output To achieve the goals, I needed to dive into the arcane art of I/O redirection in Bash. By playing correctly with the file descriptors and tee, we can implement a sort of logging level system. --- duplicity-backup.sh | 338 ++++++++++++++++++++++++++------------------ 1 file changed, 203 insertions(+), 135 deletions(-) diff --git a/duplicity-backup.sh b/duplicity-backup.sh index fbc3060..e02777c 100755 --- a/duplicity-backup.sh +++ b/duplicity-backup.sh @@ -71,9 +71,12 @@ echo "USAGE: --backup-script automatically backup the script and secret key(s) to the current working directory + -q, --quiet standard output is only written to the logfile and + is not shown. standard error will still be logged and + shown. + -n, --dry-run perform a trial run with no changes made -d, --debug echo duplicity commands to logfile - -V, --version print version information about this script and duplicity CURRENT SCRIPT VARIABLES: @@ -83,7 +86,7 @@ echo "USAGE: EXCLIST (directories excluded) = ${EXCLIST[*]:0} ROOT (root directory of backup) = ${ROOT} LOGFILE (log file path) = ${LOGFILE} -" +" >&4 } DUPLICITY="$(which duplicity)" @@ -93,7 +96,6 @@ if [ ! -x "${DUPLICITY}" ]; then exit 1 fi - version(){ # Read the version string from the file VERSION VERSION=$(&2 + echo "Aborting..." >&2 + exit 1 + else + echo "Directory ${LOGDIR} successfully created." + fi + echo "Attempting to change owner:group of ${LOGDIR} to ${LOG_FILE_OWNER} ..." + if ! chown "${LOG_FILE_OWNER}" "${LOGDIR}"; then + echo "User ${USER} could not change the owner:group of ${LOGDIR} to ${LOG_FILE_OWNER}" >&2 + echo "Aborting..." >&2 + exit 1 + else + echo "Directory ${LOGDIR} successfully changed to owner:group of ${LOG_FILE_OWNER}" + fi +elif [ ! -w "${LOGDIR}" ]; then + echo "Log directory ${LOGDIR} is not writeable by this user: ${USER}" >&2 + echo "Aborting..." >&2 + exit 1 +fi + +# -------------------- Setup I/O redirections -------------------- +# Magic from +# http://superuser.com/questions/86915/force-bash-script-to-use-tee-without-piping-from-the-command-line +# +# ##### Redirection matrix in the case when quiet mode is ON ##### +# +# QUIET mode ON | shown on screen | not shown on screen +# ---------------+-----------------+---------------------- +# logged | fd2 and fd3 | fd1, fd5 +# not logged | fd4 | - +# +# fd1 is stdout and is not shown if QUIET, but still logged +# fd2 is stderr and is always shown on screen and logged +# fd3 is like stdout but always shown on screen +# fd4 is always shown on sceen but never logged (for interactive prompts) +# +# ##### Redirection matrix in the case when quiet mode is OFF ##### +# +# QUIET mode OFF | shown on screen | not shown on screen +# ---------------+-----------------+---------------------- +# logged | fd1, fd2, fd3 | fd5 +# not logged | fd4 | - +# +# fd1 is stdout and is always logged but only shown if not QUIET +# fd2 is stderr and is always shown on screen and logged +# fd3 is like stdout but always shown on screen (for interactive prompts) +# fd4 is always shown on sceen but never logged (for the usage text) +# fd5 is never shown on screen but always logged (for delimiters in the log) +# + +# fd2 and fd3 are always logged and shown on screen via tee + +# make a backup of stderr for later +exec 6>&2 + +# for fd2 (original stderr) the output of tee needs to be redirected to stderr +exec 2> >(tee -ia "${LOGFILE}" >&2) + +# here we can leave the output of tee to stdout +exec 3> >(tee -ia "${LOGFILE}") + +# create fd4 as a copy of stdout, but that won't be redirected to tee +# so that it is always shown and never logged +exec 4>&1 + +# create fd5 as a direct redirection to the logfile +# so that the content is never shown on screen but always logged +exec 5>> "${LOGFILE}" + +# finally we modify stdout (fd1) to always being logged (like fd3 and fd5) +# but only being shown on screen if quiet mode is not active +if [[ ${QUIET} == 1 ]]; then + # Quiet mode: stdout not shown on screen but still logged via fd5 + exec 1>&5 +else + # Normal mode: stdout shown on screen and logged via fd3 + exec 1>&3 +fi + +# tests for debugging the magic +#echo "redirected to fd1" +#echo "redirected to fd2" >&2 +#echo "redirected to fd3" >&3 +#echo "redirected to fd4" >&4 +#echo "redirected to fd5" >&5 + +# ------------------------- Setting up variables ------------------------ + STATIC_OPTIONS="${DRY_RUN}${STATIC_OPTIONS}" SIGN_PASSPHRASE=${PASSPHRASE} @@ -223,11 +334,6 @@ if [[ -n "${TMPDIR}" ]]; then export TMPDIR fi -# Ensure a trailing slash always exists in the log directory name -LOGDIR="${LOGDIR%/}/" - -LOGFILE="${LOGDIR}${LOG_FILE}" - # File to use as a lock. The lock is used to insure that only one instance of # the script is running at a time. LOCKFILE=${LOGDIR}backup.lock @@ -311,7 +417,8 @@ config_sanity_fail() { EXPLANATION=$1 CONFIG_VAR_MSG="Oops!! ${0} was unable to run!\nWe are missing one or more important variables in the configuration file.\nCheck your configuration because it appears that something has not been set yet." - echo -e "${CONFIG_VAR_MSG}\n ${EXPLANATION}." + echo -e "${CONFIG_VAR_MSG}\n ${EXPLANATION}." >&2 + echo -e "--------------------- END ---------------------\n" >&5 exit 1 } @@ -325,7 +432,6 @@ check_variables () ${GPG_SIGN_KEY} = "foobar_gpg_key" || \ ${PASSPHRASE} = "foobar_gpg_passphrase")) ]] && \ config_sanity_fail "ENCRYPTION is set to 'yes', but GPG_ENC_KEY, GPG_SIGN_KEY, or PASSPHRASE have not been configured" - [[ ${LOGDIR} = "/home/foobar_user_name/logs/test2/" ]] && config_sanity_fail "LOGDIR must be configured" [[ ( ${DEST_IS_S3} = true && (${AWS_ACCESS_KEY_ID} = "foobar_aws_key_id" || ${AWS_SECRET_ACCESS_KEY} = "foobar_aws_access_key" )) ]] && \ config_sanity_fail "An s3 DEST has been specified, but AWS_ACCESS_KEY_ID or AWS_SECRET_ACCESS_KEY have not been configured" [[ ( ${DEST_IS_GS} = true && (${GS_ACCESS_KEY_ID} = "foobar_gcs_key_id" || ${GS_SECRET_ACCESS_KEY} = "foobar_gcs_secret_id" )) ]] && \ @@ -333,32 +439,6 @@ check_variables () [[ ! -z "${INCEXCFILE}" && ! -f ${INCEXCFILE} ]] && config_sanity_fail "The specified INCEXCFILE ${INCEXCFILE} does not exists" } -check_logdir() -{ - if [ ! -d "${LOGDIR}" ]; then - echo "Attempting to create log directory ${LOGDIR} ..." - if ! mkdir -p "${LOGDIR}"; then - echo "Log directory ${LOGDIR} could not be created by this user: ${USER}" - echo "Aborting..." - exit 1 - else - echo "Directory ${LOGDIR} successfully created." - fi - echo "Attempting to change owner:group of ${LOGDIR} to ${LOG_FILE_OWNER} ..." - if ! chown "${LOG_FILE_OWNER}" "${LOGDIR}"; then - echo "User ${USER} could not change the owner:group of ${LOGDIR} to ${LOG_FILE_OWNER}" - echo "Aborting..." - exit 1 - else - echo "Directory ${LOGDIR} successfully changed to owner:group of ${LOG_FILE_OWNER}" - fi - elif [ ! -w "${LOGDIR}" ]; then - echo "Log directory ${LOGDIR} is not writeable by this user: ${USER}" - echo "Aborting..." - exit 1 - fi -} - mailcmd_sendmail() { # based on http://linux.die.net/man/8/sendmail.sendmail echo -e "From: ${EMAIL_FROM}\nSubject: ${EMAIL_SUBJECT}\n" | cat - "${LOGFILE}" | ${MAILCMD} "${EMAIL_TO}" @@ -396,7 +476,7 @@ email_logfile() MAILCMD_BASENAME=${MAILCMD_REALPATH##*/} if [ ! -x "${MAILCMD}" ]; then - echo -e "Email couldn't be sent. ${MAIL} not available." >> "${LOGFILE}" + echo -e "Email couldn't be sent. ${MAIL} not available." >&2 else EMAIL_SUBJECT=${EMAIL_SUBJECT:="duplicity-backup ${BACKUP_STATUS:-"ERROR"} [${HOSTNAME}] ${LOG_FILE}"} case ${MAIL} in @@ -422,7 +502,7 @@ email_logfile() mailcmd_else;; esac - echo -e "Email notification sent to ${EMAIL_TO} using ${MAIL}" >> "${LOGFILE}" + echo -e "Email notification sent to ${EMAIL_TO} using ${MAIL}" fi fi } @@ -433,32 +513,32 @@ send_notification() if [ ! -z "${NOTIFICATION_SERVICE}" ]; then if [ "${NOTIFICATION_SERVICE}" = "slack" ]; then curl -X POST -H 'Content-type: application/json' --data "{\"text\": \"${NOTIFICATION_CONTENT}\", \"channel\": \"${SLACK_CHANNEL}\", \"username\": \"${SLACK_USERNAME}\", \"icon_emoji\": \":${SLACK_EMOJI}:\"}" "${SLACK_HOOK_URL}" - echo -e "Slack notification sent to channel ${SLACK_CHANNEL}" >> "${LOGFILE}" + echo -e "Slack notification sent to channel ${SLACK_CHANNEL}" elif [ "${NOTIFICATION_SERVICE}" = "ifttt" ]; then curl -X POST -H 'Content-type: application/json' --data "{\"value1\": \"${NOTIFICATION_CONTENT}\", \"value2\": \"${IFTTT_VALUE2}\"}" "${IFTTT_HOOK_URL}" - echo -e "IFTTT notification sent to Maker channel event ${IFTTT_EVENT}" >> "${LOGFILE}" + echo -e "IFTTT notification sent to Maker channel event ${IFTTT_EVENT}" elif [ "${NOTIFICATION_SERVICE}" = "pushover" ]; then curl -s \ -F "token=${PUSHOVER_TOKEN}" \ -F "user=${PUSHOVER_USER}" \ -F "message=${NOTIFICATION_CONTENT}" \ https://api.pushover.net/1/messages - echo -e "Pushover notification sent" >> "${LOGFILE}" + echo -e "Pushover notification sent" fi fi } get_lock() { - echo "Attempting to acquire lock ${LOCKFILE}" >> "${LOGFILE}" + echo "Attempting to acquire lock ${LOCKFILE}" >&5 if ( set -o noclobber; echo "$$" > "${LOCKFILE}" ) 2> /dev/null; then # The lock succeeded. Create a signal handler to remove the lock file when the process terminates. - trap 'EXITCODE=$?; echo "Removing lock. Exit code: ${EXITCODE}" >>${LOGFILE}; rm -f "${LOCKFILE}"' 0 - echo "successfully acquired lock." >> "${LOGFILE}" + trap 'EXITCODE=$?; echo "Removing lock. Exit code: ${EXITCODE}" >> ${LOGFILE}; rm -f "${LOCKFILE}"' EXIT + echo "successfully acquired lock." >&5 else # Write lock acquisition errors to log file and stderr - echo "lock failed, could not acquire ${LOCKFILE}" | tee -a "${LOGFILE}" >&2 - echo "lock held by $(cat "${LOCKFILE}")" | tee -a "${LOGFILE}" >&2 + echo "lock failed, could not acquire ${LOCKFILE}" >&2 + echo "lock held by $(cat "${LOCKFILE}")" >&2 email_logfile send_notification exit 2 @@ -467,7 +547,7 @@ get_lock() get_source_file_size() { - echo "-----------[ Source Disk Use Information ]-----------" >> "${LOGFILE}" + echo "-----------[ Source Disk Use Information ]-----------" # Patches to support spaces in paths- # Remove space as a field separator temporarily @@ -479,7 +559,7 @@ get_source_file_size() DUEXCFLAG="-I -" ;; OpenBSD) - echo "WARNING: OpenBSD du does not support exclusion, sizes may be off" >> "${LOGFILE}" + echo "WARNING: OpenBSD du does not support exclusion, sizes may be off" DUEXCFLAG="" ;; *) @@ -501,11 +581,10 @@ get_source_file_size() for include in "${DUINCLIST[@]}"; do echo -e "${DUEXCLIST}" | \ du -hs ${DUEXCFLAG} "${include}" | \ - awk '{ FS="\t"; $0=$0; print $1"\t"$2 }' \ - >> "${LOGFILE}" + awk '{ FS="\t"; $0=$0; print $1"\t"$2 }' done - echo >> "${LOGFILE}" + echo # Restore IFS IFS=$OLDIFS @@ -513,7 +592,7 @@ get_source_file_size() get_remote_file_size() { - echo "---------[ Destination Disk Use Information ]--------" >> "${LOGFILE}" + echo "---------[ Destination Disk Use Information ]--------" FRIENDLY_TYPE_NAME="" dest_type=$(echo "${DEST}" | cut -c 1,2) case $dest_type in @@ -524,8 +603,8 @@ get_remote_file_size() TMPDEST="${DEST%/${TMPDEST}}" ssh_opt=$(echo "${STATIC_OPTIONS}" |awk -vo="--ssh-options=" '{s=index($0,o); if (s) {s=substr($0,s+length(o)); m=substr(s,0,1); for (i=2; i < length(s); i++) { if (substr(s,i,1) == m && substr(s,i-1,1) != "\\\\") break; } print substr(s,2,i-2)}}') - SIZE=$(${TMPDEST%://*} "${ssh_opt}" "${TMPDEST#*//}" du -hs "${DEST#${TMPDEST}/}" | awk '{print $1}') 2>> "${LOGFILE}" - EMAIL_SUBJECT="${EMAIL_SUBJECT} ${SIZE} $(${TMPDEST%://*} "${ssh_opt}" "${TMPDEST#*//}" df -hP "${DEST#${TMPDEST}/}" | awk '{tmp=$5 " used"}END{print tmp}')" 2>> "${LOGFILE}" + SIZE=$(${TMPDEST%://*} "${ssh_opt}" "${TMPDEST#*//}" du -hs "${DEST#${TMPDEST}/}" | awk '{print $1}') + EMAIL_SUBJECT="${EMAIL_SUBJECT} ${SIZE} $(${TMPDEST%://*} "${ssh_opt}" "${TMPDEST#*//}" df -hP "${DEST#${TMPDEST}/}" | awk '{tmp=$5 " used"}END{print tmp}')" ;; "fi") FRIENDLY_TYPE_NAME="File" @@ -565,12 +644,12 @@ get_remote_file_size() esac if [[ ${FRIENDLY_TYPE_NAME} ]] ; then - echo -e "${SIZE}\t${FRIENDLY_TYPE_NAME} type backend" >> "${LOGFILE}" + echo -e "${SIZE}\t${FRIENDLY_TYPE_NAME} type backend" else - echo "Destination disk use information is currently only available for the following storage backends:" >> "${LOGFILE}" - echo "File, SSH, Amazon S3 and Google Cloud" >> "${LOGFILE}" + echo "Destination disk use information is currently only available for the following storage backends:" + echo "File, SSH, Amazon S3 and Google Cloud" fi - echo >> "${LOGFILE}" + echo } include_exclude() @@ -622,27 +701,27 @@ include_exclude() duplicity_cleanup() { - echo "----------------[ Duplicity Cleanup ]----------------" >> "${LOGFILE}" + echo "----------------[ Duplicity Cleanup ]----------------" if [[ "${CLEAN_UP_TYPE}" != "none" && ! -z ${CLEAN_UP_TYPE} && ! -z ${CLEAN_UP_VARIABLE} ]]; then { eval "${ECHO}" "${DUPLICITY}" "${CLEAN_UP_TYPE}" "${CLEAN_UP_VARIABLE}" "${STATIC_OPTIONS}" --force \ "${ENCRYPT}" \ - "${DEST}" >> "${LOGFILE}" + "${DEST}" } || { BACKUP_ERROR=1 } - echo >> "${LOGFILE}" + echo fi if [ ! -z "${REMOVE_INCREMENTALS_OLDER_THAN}" ] && [[ ${REMOVE_INCREMENTALS_OLDER_THAN} =~ ^[0-9]+$ ]]; then { eval "${ECHO}" "${DUPLICITY}" remove-all-inc-of-but-n-full "${REMOVE_INCREMENTALS_OLDER_THAN}" \ "${STATIC_OPTIONS}" --force \ "${ENCRYPT}" \ - "${DEST}" >> "${LOGFILE}" + "${DEST}" } || { BACKUP_ERROR=1 } - echo >> "${LOGFILE}" + echo fi } @@ -655,8 +734,7 @@ duplicity_backup() "${EXCLUDE}" \ "${INCLUDE}" \ "${EXCLUDEROOT}" \ - "${ROOT}" "${DEST}" \ - >> "${LOGFILE}" + "${ROOT}" "${DEST}" } || { BACKUP_ERROR=1 } @@ -666,8 +744,7 @@ duplicity_cleanup_failed() { { eval "${ECHO}" "${DUPLICITY}" "${OPTION}" "${VERBOSITY}" "${STATIC_OPTIONS}" \ - "${DEST}" \ - >> "${LOGFILE}" + "${DEST}" } || { BACKUP_ERROR=1 } @@ -676,9 +753,9 @@ duplicity_cleanup_failed() setup_passphrase() { if [ ! -z "${GPG_ENC_KEY}" ] && [ ! -z "${GPG_SIGN_KEY}" ] && [ "${GPG_ENC_KEY}" != "${GPG_SIGN_KEY}" ]; then - echo -n "Please provide the passphrase for decryption (GPG key 0x${GPG_ENC_KEY}): " + echo -n "Please provide the passphrase for decryption (GPG key 0x${GPG_ENC_KEY}): " >&3 builtin read -s -r ENCPASSPHRASE - echo -ne "\n" + echo -ne "\n" >&3 PASSPHRASE=${ENCPASSPHRASE} export PASSPHRASE fi @@ -688,24 +765,6 @@ get_file_sizes() { get_source_file_size get_remote_file_size - - case $(uname) in - FreeBSD|Darwin|DragonFly) - sed -i '' -e '/^--*$/d' "${LOGFILE}" - ;; - OpenBSD) - ed -s "${LOGFILE}" <<-"EOF" - g/^--*$/d - w - q -EOF - ;; - *) - sed -i -e '/^--*$/d' "${LOGFILE}" - ;; - esac - - [[ -n "${LOG_FILE_OWNER}" ]] && chown "${LOG_FILE_OWNER}" "${LOGFILE}" } backup_this_script() @@ -720,35 +779,36 @@ backup_this_script() TMPFILENAME=${TMPDIR}.tar.gpg README=${TMPDIR}/README - echo "You are backing up: " - echo " 1. ${SCRIPTPATH}" + echo "You are backing up: " >&3 + echo " 1. ${SCRIPTPATH}" >&3 if [ ! -z "${GPG_ENC_KEY}" ] && [ ! -z "${GPG_SIGN_KEY}" ]; then if [ "${GPG_ENC_KEY}" = "${GPG_SIGN_KEY}" ]; then - echo " 2. GPG Secret encryption and sign key: ${GPG_ENC_KEY}" + echo " 2. GPG Secret encryption and sign key: ${GPG_ENC_KEY}" >&3 else - echo " 2. GPG Secret encryption key: ${GPG_ENC_KEY} and GPG secret sign key: ${GPG_SIGN_KEY}" + echo " 2. GPG Secret encryption key: ${GPG_ENC_KEY} and GPG secret sign key: ${GPG_SIGN_KEY}" >&3 fi else - echo " 2. GPG Secret encryption and sign key: none (symmetric encryption)" + echo " 2. GPG Secret encryption and sign key: none (symmetric encryption)" >&3 fi if [ ! -z "${CONFIG}" ] && [ -f "${CONFIG}" ]; then - echo " 3. Config file: ${CONFIG}" + echo " 3. Config file: ${CONFIG}" >&3 fi if [ ! -z "${INCEXCFILE}" ] && [ -f "${INCEXCFILE}" ]; then - echo " 4. Include/Exclude globbing file: ${INCEXCFILE}" + echo " 4. Include/Exclude globbing file: ${INCEXCFILE}" >&3 fi - echo "Backup tarball will be encrypted and saved to: $(pwd)/${TMPFILENAME}" - echo - echo ">> Are you sure you want to do that ('yes' to continue)?" + echo "Backup tarball will be encrypted and saved to: $(pwd)/${TMPFILENAME}" >&3 + echo >&3 + echo ">> Are you sure you want to do that ('yes' to continue)?" >&3 read -r ANSWER if [ "${ANSWER}" != "yes" ]; then - echo "You said << ${ANSWER} >> so I am exiting now." + echo "You said << ${ANSWER} >> so I am exiting now." >&3 + echo -e "--------------------- END ---------------------\n" >&5 exit 1 fi @@ -777,19 +837,22 @@ backup_this_script() fi echo -e "${README_TXT}" > "${README}" - echo "Encrypting tarball, choose a password you'll remember..." + echo "Encrypting tarball, choose a password you'll remember..." >&3 tar -cf - "${TMPDIR}" | gpg -aco "${TMPFILENAME}" rm -Rf "${TMPDIR}" - echo -e "\nIMPORTANT!!" - echo ">> To restore these files, run the following (remember your password):" - echo "gpg -d ${TMPFILENAME} | tar -xf -" - echo -e "\nYou may want to write the above down and save it with the file." + echo -e "\nIMPORTANT!!" >&3 + echo ">> To restore these files, run the following (remember your password):" >&3 + echo "gpg -d ${TMPFILENAME} | tar -xf -" >&3 + echo -e "\nYou may want to write the above down and save it with the file." >&3 } -check_variables -check_logdir +# ################################################## +# #### end of functions definition #### +# ################################################## -echo -e "-------- START DUPLICITY-BACKUP SCRIPT for ${HOSTNAME} --------\n" >> "${LOGFILE}" +check_variables + +echo -e "-------- START DUPLICITY-BACKUP SCRIPT for ${HOSTNAME} --------\n" >&5 get_lock @@ -800,7 +863,7 @@ EXCLUDEROOT= case "${COMMAND}" in "backup-script") backup_this_script - exit + exit 0 ;; "full") @@ -817,11 +880,12 @@ case "${COMMAND}" in DEST=${OLDROOT} OPTION="verify" - echo -e "-------[ Verifying Source & Destination ]-------\n" >> "${LOGFILE}" + echo -e "-------[ Verifying Source & Destination ]-------\n" include_exclude setup_passphrase - echo -e "Attempting to verify now ..." + echo -e "Attempting to verify now ...\n" >&3 duplicity_backup + echo OLDROOT=${ROOT} ROOT=${DEST} @@ -829,7 +893,7 @@ case "${COMMAND}" in get_file_sizes - echo -e "Verify complete. Check the log file for results:\n>> ${LOGFILE}" + echo -e "Verify complete.\n" >&3 ;; "cleanup") @@ -839,11 +903,11 @@ case "${COMMAND}" in STATIC_OPTIONS="--force" fi - echo -e "-------[ Cleaning up Destination ]-------\n" >> "${LOGFILE}" + echo -e "-------[ Cleaning up Destination ]-------\n" setup_passphrase duplicity_cleanup_failed - echo -e "Cleanup complete." >> "${LOGFILE}" + echo -e "Cleanup complete." ;; "restore") @@ -854,15 +918,16 @@ case "${COMMAND}" in fi if [[ ! "${RESTORE_DEST}" ]]; then - echo "Please provide a destination path (eg, /home/user/dir):" + echo "Please provide a destination path (eg, /home/user/dir):" >&3 read -r -e NEWDESTINATION DEST=${NEWDESTINATION} - echo ">> You will restore from ${ROOT} to ${DEST}" - echo "Are you sure you want to do that ('yes' to continue)?" + echo ">> You will restore from ${ROOT} to ${DEST}" >&3 + echo "Are you sure you want to do that ('yes' to continue)?" >&3 read -r ANSWER if [[ "${ANSWER}" != "yes" ]]; then - echo "You said << ${ANSWER} >> so I am exiting now." - echo -e "User aborted restore process ...\n" >> "${LOGFILE}" + echo "You said << ${ANSWER} >> so I am exiting now." >&3 + echo -e "User aborted restore process ...\n" >&2 + echo -e "--------------------- END ---------------------\n" >&5 exit 1 fi else @@ -870,7 +935,7 @@ case "${COMMAND}" in fi setup_passphrase - echo "Attempting to restore now ..." + echo "Attempting to restore now ..." >&3 duplicity_backup ;; @@ -883,8 +948,8 @@ case "${COMMAND}" in fi if [[ ! "${FILE_TO_RESTORE}" ]]; then - echo "Which file or directory do you want to restore?" - echo "(give the path relative to the root of the backup eg, mail/letter.txt):" + echo "Which file or directory do you want to restore?" >&3 + echo "(give the path relative to the root of the backup eg, mail/letter.txt):" >&3 read -r -e FILE_TO_RESTORE echo fi @@ -895,14 +960,15 @@ case "${COMMAND}" in DEST=$(basename "${FILE_TO_RESTORE}") fi - echo -e "YOU ARE ABOUT TO..." - echo -e ">> RESTORE: ${FILE_TO_RESTORE}" - echo -e ">> TO: ${DEST}" - echo -e "\nAre you sure you want to do that ('yes' to continue)?" + echo -e "YOU ARE ABOUT TO..." >&3 + echo -e ">> RESTORE: ${FILE_TO_RESTORE}" >&3 + echo -e ">> TO: ${DEST}" >&3 + echo -e "\nAre you sure you want to do that ('yes' to continue)?" >&3 read -r ANSWER if [ "${ANSWER}" != "yes" ]; then - echo "You said << ${ANSWER} >> so I am exiting now." - echo -e "--------------------- END ---------------------\n" >> "${LOGFILE}" + echo "You said << ${ANSWER} >> so I am exiting now." >&3 + echo -e "User aborted restore process ...\n" >&2 + echo -e "--------------------- END ---------------------\n" >&5 exit 1 fi @@ -910,7 +976,7 @@ case "${COMMAND}" in DEST="'${DEST}'" setup_passphrase - echo "Restoring now ..." + echo "Restoring now ..." >&3 #use INCLUDE variable without creating another one INCLUDE="--file-to-restore ${FILE_TO_RESTORE}" duplicity_backup @@ -926,8 +992,7 @@ case "${COMMAND}" in eval \ "${DUPLICITY}" "${OPTION}" "${VERBOSITY}" "${STATIC_OPTIONS}" \ ${ENCRYPT} \ - "${DEST}" | tee -a "${LOGFILE}" - echo -e "--------------------- END ---------------------\n" >> "${LOGFILE}" + "${DEST}" ;; "collection-status") @@ -936,8 +1001,7 @@ case "${COMMAND}" in eval \ "${DUPLICITY}" "${OPTION}" "${VERBOSITY}" "${STATIC_OPTIONS}" \ ${ENCRYPT} \ - "${DEST}" | tee -a "${LOGFILE}" - echo -e "--------------------- END ---------------------\n" >> "${LOGFILE}" + "${DEST}" ;; "backup") @@ -948,12 +1012,12 @@ case "${COMMAND}" in ;; *) - echo -e "[Only show $(basename "$0") usage options]\n" >> "${LOGFILE}" + echo -e "[Only show $(basename "$0") usage options]\n" usage ;; esac -echo -e "--------- END DUPLICITY-BACKUP SCRIPT ---------\n" >> "${LOGFILE}" +echo -e "--------- END DUPLICITY-BACKUP SCRIPT ---------\n" >&5 if [ "${BACKUP_ERROR}" ]; then BACKUP_STATUS="ERROR" @@ -983,4 +1047,8 @@ unset PASSPHRASE unset SIGN_PASSPHRASE unset FTP_PASSWORD +# restore stdout and stderr to their original values +# and close the other fd +exec 1>&4 2>&6 3>&- 4>&- 5>&- 6>&- + # vim: set tabstop=2 shiftwidth=2 sts=2 autoindent smartindent: From 5d2012863444364a6e980ab2a69ee9b6de2f589a Mon Sep 17 00:00:00 2001 From: zertrin Date: Sat, 3 Sep 2016 23:43:19 +0200 Subject: [PATCH 2/2] Improve some of the new comments about the new logging --- duplicity-backup.sh | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/duplicity-backup.sh b/duplicity-backup.sh index e02777c..2e2d68c 100755 --- a/duplicity-backup.sh +++ b/duplicity-backup.sh @@ -71,9 +71,9 @@ echo "USAGE: --backup-script automatically backup the script and secret key(s) to the current working directory - -q, --quiet standard output is only written to the logfile and - is not shown. standard error will still be logged and - shown. + -q, --quiet silence most of output messages, except errors and output + that is intended for interactive usage. Silenced output + is still logged in the logfile. -n, --dry-run perform a trial run with no changes made -d, --debug echo duplicity commands to logfile @@ -251,14 +251,9 @@ fi # # QUIET mode ON | shown on screen | not shown on screen # ---------------+-----------------+---------------------- -# logged | fd2 and fd3 | fd1, fd5 +# logged | fd2, fd3 | fd1, fd5 # not logged | fd4 | - # -# fd1 is stdout and is not shown if QUIET, but still logged -# fd2 is stderr and is always shown on screen and logged -# fd3 is like stdout but always shown on screen -# fd4 is always shown on sceen but never logged (for interactive prompts) -# # ##### Redirection matrix in the case when quiet mode is OFF ##### # # QUIET mode OFF | shown on screen | not shown on screen @@ -273,15 +268,13 @@ fi # fd5 is never shown on screen but always logged (for delimiters in the log) # -# fd2 and fd3 are always logged and shown on screen via tee - # make a backup of stderr for later exec 6>&2 +# fd2 and fd3 are always logged and shown on screen via tee # for fd2 (original stderr) the output of tee needs to be redirected to stderr exec 2> >(tee -ia "${LOGFILE}" >&2) - -# here we can leave the output of tee to stdout +# create fd3 as a redirection to stdout and the logfile via tee exec 3> >(tee -ia "${LOGFILE}") # create fd4 as a copy of stdout, but that won't be redirected to tee