Skip to content
Snippets Groups Projects
Commit 526c3c00 authored by Hahn Axel (hahn)'s avatar Hahn Axel (hahn)
Browse files

delete legacy scripts

parent 0e05aaf6
No related branches found
No related tags found
1 merge request!7move configs for localdump
#!/bin/bash
# ================================================================================
#
# RESTORE - interactive restore
#
# Restore duplicity backup data to a local directory.
# Please notice: duplicity does not overwrite any dir or file. So the restore
# path is different to the path of the backed up data.
#
# SYNTAX:
# restore.sh [path-to-restore]
#
# path-to-restore - optional
# default: none ... you get an interactive selection of backup sets
#
# --------------------------------------------------------------------------------
# ah - Axel Hahn <axel.hahn@iml.unibe.ch>
# ds - Daniel Schueler <daniel.schueler@iml.unibe.ch>
#
# 2016-11-17 ah,ds v1.0
# 2017-10-11 ah,ds v1.1 added support for duplicity param --ssh-backend
# 2018-08-27 ah,ds v1.2 fix restore target with a given selection; handle '*' placeholder
# 2019-06-05 ah,ds v1.3 add custom cache dir
# ================================================================================
# --------------------------------------------------------------------------------
# CONFIG
# --------------------------------------------------------------------------------
# . `dirname $0`/inc_config.sh
. `dirname $0`/jobhelper.sh
. `dirname $0`/inc_bash.sh
STORAGE_BASEDIR=`_j_getvar ${STORAGEFILE} "storage"`
# check
if [ -z $STORAGE_BASEDIR ]; then
echo ERROR: missing config for backup target.
echo There must be an entry storage in ${STORAGEFILE}
exit 1
fi
# ----- read something from config files
RESTORE_BASEDIR=`_j_getvar ${STORAGEFILE} "restore-path"`
PASSPHRASE=`_j_getvar ${STORAGEFILE} "gnupg-passphrase"`
export PASSPHRASE
sFileSshPrivkey=`_j_getvar ${STORAGEFILE} "ssh-privatekey"`
sParams=
# task#1623 - fallback ssh backend for Debian 8
sSshBackend=`_j_getvar ${STORAGEFILE} "ssh-backend"`
if [ ! -z $sSshBackend ]; then
sParams="${sParams} --ssh-backend $sSshBackend"
fi
sFileSshPrivkey=`_j_getvar ${STORAGEFILE} "ssh-privatekey"`
if [ ! -z $sFileSshPrivkey ]; then
sParams="${sParams} --ssh-options="""-oIdentityFile=${sFileSshPrivkey}""" "
fi
# task#3046 - add custom cache dir
sCacheDir=`_j_getvar ${STORAGEFILE} "cachedir"`
if [ ! -z $sCacheDir ]; then
sParams="${sParams} --archive-dir=$sCacheDir"
fi
# ----- what to restore ...
sDir2restore=
sRestoreItem=
sDate=`date +%Y-%m-%d`
# duplicity target
sTarget=
# --------------------------------------------------------------------------------
# FUNCTIONS
# --------------------------------------------------------------------------------
# ----------------------------------------------------------------------
# enter name of directory to restore; menu item
# ----------------------------------------------------------------------
function enterBackupDir(){
h2 "select a directory to be restored"
echo "This is a list of the directories from the backup configuration."
echo "Enter the full path"
echo
echo "Enter the full path; marked entries with (*) do not exist on this machine"
echo
sDirs="`j_getDirs2Backup`"
for mydir in $sDirs
do
if [ ! -d "$mydir" ]; then
color error
echo "$mydir (*)"
color reset
else
echo "$mydir"
fi
done
color input
echo -n "[$sDir2restore] >"
color reset
read myinput
setBackupDir "${myinput}"
}
# ----------------------------------------------------------------------
# enter date to restore from; menu item
# ----------------------------------------------------------------------
function enterDate(){
h2 "edit date to restore"
echo "The acceptible time strings are intervals (like \"3D64s\"), w3-datetime"
echo "strings, like \"2002-04-26T04:22:01-07:00\" (strings like"
echo "\"2002-04-26T04:22:01\" are also acceptable - duplicity will use the"
echo "current time zone), or ordinary dates like 2/4/1997 or 2001-04-23"
echo "(various combinations are acceptable, but the month always precedes"
echo "the day)."
echo
echo "today in YYYY-MM-DD: `date +%Y-%m-%d`"
echo
color input
echo -n "[$sDate] >"
color reset
read sNewDate
if [ ! -z $sNewDate ]; then
sDate=$sNewDate
fi
}
# ----------------------------------------------------------------------
# enter relative path to restore; menu item
# ----------------------------------------------------------------------
function enterRestoreitem(){
h2 "set restore item (path)"
echo "enter a relative path behind ${sDir2restore}/"
echo "empty means: all data"
echo
color input
echo -n "[$sRestoreItem] >"
color reset
read sNewsRestoreItem
if [ ! -z $sNewsRestoreItem ]; then
sRestoreItem=$sNewsRestoreItem
setVars
fi
getRemoteFiles
}
# ----------------------------------------------------------------------
# search a file in backup; menu item
# ----------------------------------------------------------------------
function searchFile(){
h2 "search a file in backup --time $sDate"
echo "Enter something that was backuped from ${sDir2restore}"
echo "searchtext matches anywhere in filename or path - it is a grep grep in"
echo "duplicity list-current-files --time $sDate [target]"
echo "Press just Return to exit"
echo
color input
echo -n "search for >"
color reset
read sSearch
if [ ! -z $sSearch ]; then
getRemoteFiles $sSearch
fi
}
# ----------------------------------------------------------------------
# verify backup set with local files; menu item
# ----------------------------------------------------------------------
function verify(){
h2 "verify"
echo duplicity verify ${sParams} ${sTarget} -v4 $sDir2restore
color cmd
duplicity verify ${sParams} ${sTarget} -v4 $sDir2restore
fetchrc
color reset
}
# ----------------------------------------------------------------------
# show file changes in backupsets; menu item
# work in progress ... I don't know how to match a file...
# ----------------------------------------------------------------------
function showFilechanges(){
tmpcache=~/.cache/duplicity-changes-${sSafeName}.txt
h2 "show file changes"
egrep "\ \ (changed|deleted|new)\ \ " `fgrep -l "Localdir ${sDir2restore}" ~/.cache/duplicity/*/*.manifest` >$tmpcache
# ls -l $tmpcache
# head $tmpcache
echo "I hope you made a search first"
echo "Enter filename with complete path that was backuped from ${sDir2restore}"
echo "Press just Return to exit"
echo
color input
echo -n "search for >"
color reset
read sSearch
if [ ! -z "${sSearch}" ]; then
echo grep "${sSearch}"
color cmd
# duplicity collection-status --file-changed "`basename ${sSearch}`" ${sParams} ${sTarget}/`dirname "${sSearch}"`
cat $tmpcache | grep "${sSearch}" | while read line
do
sManifest=`echo $line | cut -f 1 -d ":"`
# duplicity-inc.20161111T152106Z.to.20161111T152141Z.manifest
sTime=`basename "${sManifest}" | cut -f 2 -d "."`
sOut=`echo $line | cut -f 2- -d ":"`
echo $sTime $sOut | sed "s#new#new #"
done | sort
echo
color reset
fi
}
# ----------------------------------------------------------------------
# show remote volumes with incremental and full backups
# ----------------------------------------------------------------------
function getRemoteVolumes(){
tmpoutVolumes=/tmp/outvolumelist_$$
h2 "volumes for $sDir2restore"
echo duplicity collection-status ${sParams} ${sTarget}
color cmd
duplicity collection-status ${sParams} ${sTarget} | tee -a $tmpoutVolumes
fetchrc
color reset
echo
if [ `cat $tmpoutVolumes | egrep "(Full|Incremental)" | wc -l` -eq 0 ]; then
color error
echo "ERROR: no backup sets were found for directory [$sDir2restore]"
echo
color reset
sDir2restore=
setVars
else
color ok
echo "OK, `cat $tmpoutVolumes | grep "Full" | wc -l` Full and `cat $tmpoutVolumes | grep "Incremental" | wc -l` incremental backups"
color reset
fi
rm -f $tmpoutVolumes
}
# ----------------------------------------------------------------------
# list files from backupset
# ----------------------------------------------------------------------
function getRemoteFiles(){
iLines=50
tmpout=/tmp/outfilelist_$$
sSearch=$1
if [ -z $sSearch ]; then
sSearch="^...\ ...\ ..\ ........\ ....\ $sRestoreItem"
fi
echo
echo
echo --- files ... filtered for date [$sDate] by [$sSearch]
echo duplicity list-current-files --time $sDate ${sParams} ${sTarget}
color cmd
duplicity list-current-files --time $sDate ${sParams} ${sTarget} 2>&1 | grep "$sSearch" | tee -a $tmpout | head -$iLines
fetchrc
color reset
if [ `cat $tmpout | wc -l` -eq 0 ]; then
color error
echo ERROR: your file does not match ... try another time ... or another search string
color reset
else
echo ... max $iLines lines ... maybe the output was cut
echo
if [ -z $1 ]; then
echo
doRestoreDryRun
echo
echo
fi
fi
rm -f $tmpout
}
# ----------------------------------------------------------------------
# set backup dir .. called when using cli parameter or enterBackupDir
# param string full path of a directory to retore
# ----------------------------------------------------------------------
function setBackupDir(){
if [ ! -z $1 ]; then
sDirs2Backup="`j_getDirs2Backup`"
bFound=0
for mydir in ${sDirs2Backup}
do
if [ "${mydir}" = "${1}" ]; then
bFound=1
fi
done
if [ $bFound -eq 0 ]; then
color error
echo
echo "WARNING: seems to be wrong ... "
echo "At the moment I restore only known targets."
color reset
# TODO: if we want to retore other backup sets of other servers
# we cannot be as strict
else
sDir2restore=$1
setVars
getRemoteVolumes
fi
fi
}
# ----------------------------------------------------------------------
# internal: set variables for target path and backup set to make
# duplicity calls
# ----------------------------------------------------------------------
function setVars(){
sSafeName=`j_getSafename "$sDir2restore"`
sTarget=`j_getFullTarget "$sDir2restore"`
sRestorepath="${RESTORE_BASEDIR}/${sSafeName}"
if [ ! -z $sRestoreItem ]; then
echo ${sRestoreItem} | grep '\*' >/dev/null
if [ $? -eq 0 ]; then
sRestoreItem=`dirname $sRestoreItem | sed 's#^\.##'`
color error
echo ERROR: using a placeholder is not allowed. Using the directory above.
echo [$sRestoreItem]
color reset
fi
sRestorepath="${sRestorepath}/${sRestoreItem}"
fi
}
# ----------------------------------------------------------------------
# restore dry run ... called in getRemoteFiles
# ----------------------------------------------------------------------
function doRestoreDryRun(){
echo "--- dry run"
echo duplicity restore --dry-run --file-to-restore "$sRestoreItem" --time $sDate ${sParams} ${sTarget} ${sRestorepath}
color cmd
duplicity restore --dry-run --file-to-restore "$sRestoreItem" --time $sDate ${sParams} ${sTarget} ${sRestorepath}
fetchrc
color reset
}
# ----------------------------------------------------------------------
# restore and finish the script; menu item
# ----------------------------------------------------------------------
function doRestore(){
mkdir -p $sRestorepath
h2 "RESTORE"
echo duplicity restore --file-to-restore "$sRestoreItem" --time $sDate ${sParams} ${sTarget} ${sRestorepath}
color cmd
duplicity restore --file-to-restore "$sRestoreItem" --time $sDate ${sParams} ${sTarget} ${sRestorepath}
fetchrc
color reset
echo
echo
echo Restore is finished.
echo Have look to the output above.
echo "The restore path has `find ${sRestorepath} -type f | wc -l` files (`du -hs ${sRestorepath} | awk '{ print $1 }'`)"
echo
echo find ${sRestorepath}
echo
exit 0
}
# --------------------------------------------------------------------------------
# MAIN
# --------------------------------------------------------------------------------
h1 "RESTORE - GET FILES FROM STORAGE"
# ----- Check requirements
j_requireUser "root"
j_requireBinary "duplicity"
# ----- set a directory to restore to have a useful initial point
setBackupDir $1
if [ -z $sDir2restore ]; then
enterBackupDir
fi
# ----- menu and loop
while true
do
h1 "Restore :: Menu"
# getRemoteVolumes
# getRemoteFiles
echo
echo " D - directory to restore: $sDir2restore"
echo " T - time : $sDate"
echo " W - what to restore : $sRestoreItem"
echo
echo " C - show file changes"
echo " S - search file"
echo " V - verify"
echo " B - Bash (Shell)"
echo
echo " R - start restore"
echo
echo " retore from : $sTarget"
echo " retore to : $sRestorepath"
echo -n " "
ls -d $sRestorepath >/dev/null 2>&1
if [ $? -eq 0 ]; then
color error
echo "WARNING: directory already exists! Backup will fail."
color reset
else
echo "OK, it does not exist yet"
fi
echo
echo " X - exit"
echo
color input
echo -n "Select (not case sensitive) --> "
color reset
read action
echo
case $action in
d|D)
enterBackupDir
;;
t|T)
enterDate
;;
c|C)
showFilechanges
;;
s|S)
searchFile
;;
v|V)
verify
;;
b|B)
echo type exit to return...
export PS1="[`basename $0` \u@\h \w]\$ "
bash
;;
w|W)
enterRestoreitem
;;
r|R)
doRestore
;;
x|X)
exit 0
;;
*)
echo "Try again"
esac
done
# --------------------------------------------------------------------------------
#!/bin/bash
# ================================================================================
#
# TRANSFER LOCAL DATA TO BACKUP STORAGE
#
# SYNTAX:
# transfer.sh - incremental backup
# transfer.sh full - full backup
# transfer.sh dumps - transfer local dumps only
#
# duplicity see
# - man pages http://duplicity.nongnu.org/duplicity.1.html
# - example http://duplicity.nongnu.org/contrib/jwfull
# - example http://duplicity.nongnu.org/contrib/jwincr
#
# this script was build to support ssh backend only
#
# --------------------------------------------------------------------------------
# ah - Axel Hahn <axel.hahn@iml.unibe.ch>
# ds - Daniel Schueler <daniel.schueler@iml.unibe.ch>
#
# 2016-11-10 ah,ds v1.0
# 2016-12-19 ah,ds v1.1 added parameter "dumps"
# 2017-02-16 ah,ds v1.2 added support for storage slots
# 2017-10-11 ah,ds v1.3 added support for duplicity param --ssh-backend
# 2017-10-17 ah,ds v1.4 remove PIPESTATUS for Debian8 compatibility
# 2017-11-17 ah,ds v1.5 check pid of lockfile in process list if process still runs
# 2018-06-19 ah,ds v1.6 replace --exclude with --exclude regexp in custom dirs
# 2019-06-05 ah,ds v1.7 add custom cache dir
# 2019-09-09 ah,ds v1.8 add testfile on target
# 2019-10-30 ah,ds v1.9 for rsync targets: create remote target dir with ssh command
# 2020-01-21 ah,ds v1.10 show colored OK or FAILED at the end of output
# 2020-02-25 ah,ds, v1.11 fix test -z with non existing vars; show final backup status
# 2021-01-29 ah,ds, v1.12 abort on empty passphrase
# ================================================================================
# --------------------------------------------------------------------------------
# CONFIG
# --------------------------------------------------------------------------------
# . `dirname $0`/inc_config.sh
. `dirname $0`/jobhelper.sh
. `dirname $0`/inc_bash.sh
typeset -i rc=0
STORAGE_BASEDIR=`_j_getvar ${STORAGEFILE} "storage"`
STORAGE_TESTFILE=`_j_getvar ${STORAGEFILE} "storage-file"`
STORAGE_KEEP=`_j_getvar ${STORAGEFILE} "keep"`
STORAGE_VOLSIZE=`_j_getvar ${STORAGEFILE} "volsize"`
# check
if [ -z "$STORAGE_BASEDIR" ]; then
color error
echo ERROR: missing config for backup target.
echo There must be an entry storage in ${STORAGEFILE}
color reset
exit 1
fi
if [ ! -z "$STORAGE_TESTFILE" -a ! -f "$STORAGE_TESTFILE" ]; then
color error
echo ERROR: missing testfile $STORAGE_TESTFILE on backup target.
echo The Backup target disk / NAS is not mounted.
color reset
exit 1
fi
if [ -z "$STORAGE_KEEP" ]; then
color error
echo ERROR: missing config for backup target.
echo There must be an entry keep in ${STORAGEFILE}
color reset
exit 1
fi
# METHOD incremental is default; full backup will be triggered with
# first param "full"
METHOD=
transferlog="${DIR_LOGS}/transfer-`date +%Y%m%d`.log"
lockfile="${DIR_LOGS}/transfer.running"
rcfile=/tmp/transfer-rc.$$.tmp
# --------------------------------------------------------------------------------
# FUNCTIONS
# --------------------------------------------------------------------------------
# --------------------------------------------------------------------------------
# MAIN
# --------------------------------------------------------------------------------
h1 `date` TRANSFER LOCAL DATA TO STORAGE | tee -a $transferlog
echo METHOD: $METHOD | tee -a $transferlog
echo TARGET: ${STORAGE_BASEDIR} | tee -a $transferlog
echo | tee -a $transferlog
# ----- Check requirements
j_requireUser "root"
j_requireBinary "duplicity"
echo Check locking of a running transfer
if [ -f "${lockfile}" ]; then
color error
echo A lock file for a running transfer was found
cat "${lockfile}"
color reset
echo
# 1659 - check process id inside the lock file
# detect pid from lockfile and search for this process
lockpid=`cat "${lockfile}" | cut -f 2 -d "-" | cut -f 4 -d " " | grep "[0-9]"`
if [ -z $lockpid ]; then
color error
echo ERROR: pid was not fetched from lock file. Check the transfer processes manually, please.
color reset
exit 1
fi
echo transfer processes with pid or ppid ${lockpid}:
color cmd
ps -ef | grep $lockpid | grep "transfer"
rccheck=$?
color reset
if [ $rccheck -eq 0 ]; then
color error
echo ERROR: The transfer with pid $lockpid seems to be still active. Aborting.
color reset
exit 1
fi
color ok
echo OK, the transfer seems not to be active anymore. I IGNORE the lock and continue...
color reset
fi
echo Creating a lock file ...
echo "transfer started `date` - process id $$" > "${lockfile}"
if [ $? -ne 0 ]; then
color error
echo ABORT - unable to create transfer lock
color reset
exit 2
fi
# ----- BACKUP VARS
# parameters for all
sParams=
if [ "$1" = "dumps" ]; then
sDirs2Backup="`_j_getvar ${DIRFILE} dir-localdumps`"
else
sDirs2Backup="`j_getDirs2Backup`"
fi
if [ "$1" = "full" ]; then
METHOD="full"
fi
if [ "$1" = "auto" ]; then
METHOD="--full-if-older-than $2"
fi
PASSPHRASE=`_j_getvar ${STORAGEFILE} "gnupg-passphrase"`
if [ -z "${PASSPHRASE}" ]; then
echo "ERROR: no value gnupg-passphrase was set in ${STORAGEFILE} to encrypt backup data."
echo "Aborting."
exit 1
fi
sParamExclude=
# build exclude param list for duplicity
#
# REMARK: the excludes must fit the include definition
# On non matching excludes duplicity stops
#
for sItem in `_j_getvar ${DIRFILE} exclude`
do
sParamExclude="$sParamExclude --exclude-regexp """$sItem""" "
done
#
# sExcludefile="${DIR_JOBS}/transfer-exclude.txt"
#
# if [ -f ${sExcludefile} ]; then
# sParamExclude="$sParamExclude --exclude-filelist """${sExcludefile}""" "
# fi
export PASSPHRASE
# task#1623 - fallback ssh backend for Debian 8
sSshBackend=`_j_getvar ${STORAGEFILE} "ssh-backend"`
if [ ! -z $sSshBackend ]; then
sParams="${sParams} --ssh-backend $sSshBackend"
fi
sFileSshPrivkey=`_j_getvar ${STORAGEFILE} "ssh-privatekey"`
if [ ! -z $sFileSshPrivkey ]; then
sParams="${sParams} --ssh-options="""-oIdentityFile=${sFileSshPrivkey}""" "
fi
# task#3046 - add custom cache dir
sCacheDir=`_j_getvar ${STORAGEFILE} "cachedir"`
if [ ! -z $sCacheDir ]; then
sParams="${sParams} --archive-dir=$sCacheDir"
if [ ! -d $sCacheDir ]; then
mkdir -p $sCacheDir
chmod 750 $sCacheDir
fi
fi
# ----- Wait for a free slot
j_transferStart | tee -a $transferlog
# ----- START BACKUPS
(
for mydir in $sDirs2Backup
do
# remove ending slash ... otherwise duplicity will fail
mydir=`echo $mydir | sed 's#\/$##g'`
if [ -d "$mydir" ]; then
h2 "`date` STORE $mydir"
# --- build parameters for duplicity
sSafeName=`j_getSafename "$mydir"`
sTarget=`j_getFullTarget "$mydir"`
sBackupParams=
sBackupParams="${sBackupParams} ${sParams} "
sBackupParams="${sBackupParams} ${sParamExclude} "
if [ ! -z $STORAGE_VOLSIZE ]; then
sBackupParams="${sBackupParams} --volsize ${STORAGE_VOLSIZE} "
fi
# verbosity level to fetch changed files from log
sBackupParams="${sBackupParams} -v8"
# add asynchronous upload
sBackupParams="${sBackupParams} --asynchronous-upload"
# ---------- START --------------------
# detect custom backup sets and add its includes and excludes
backupid=`j_getSetnameOfPath "$mydir"`
sSpaceReplace="___SPACE___"
if [ ! -z $backupid ]; then
for param in include exclude
do
for sItem in `_j_getvar ${DIRFILE} "${backupid}\-\-${param}" | sed "s#\ #${sSpaceReplace}#g"`
do
sBackupParams="${sBackupParams} --${param}-regexp """$sItem""" "
done
done
sBackupParams=`echo ${sBackupParams} | sed "s#${sSpaceReplace}# #g"`
fi
# ---------- ENDE --------------------
# --- for rsync only: create remote directory
echo ${sTarget} | fgrep "rsync://" >/dev/null
if [ $? -eq 0 ]; then
# sshTarget=`echo ${sTarget} | sed "s#rsync://#scp://#"`
# echo Creating remote directory with fetching collection-status on $sshTarget
# color cmd
# duplicity collection-status ${sParams} ${sshTarget}
# color reset
sshTarget=`echo ${sTarget} | cut -f 3 -d '/'`
RemoteDir=`echo ${sTarget} | cut -f 4- -d '/'`
cmd="ssh"
if [ ! -z ${sFileSshPrivkey} ]; then
cmd="${cmd} -i ${sFileSshPrivkey}"
fi
cmd="${cmd} ${sshTarget} mkdir -p ${RemoteDir} 2>/dev/null ; ls -ld ${RemoteDir} "
echo Creating remote directory first ...
color cmd
$cmd
color reset
fi
# --- backup
echo backup to target: ${sTarget}
echo duplicity ${METHOD} ${sBackupParams} ${mydir} ${sTarget}
color cmd
duplicity ${METHOD} ${sBackupParams} ${mydir} ${sTarget}
fetchrc
color reset
echo
if [ $myrc -ne 0 ]; then
color error
echo DIR ERROR ${mydir} rc=$myrc during file transfer
case $myrc in
23) echo A lock file was found. Maybe this server was rebooted while performing a backup.
echo If so delete the file lockfile.lock named in the output and start $0 again.
;;
31) echo Maybe you it is a problem with the gpg-agent.conf
ls -l ~/.gnupg/gpg-agent.conf && cat ~/.gnupg/gpg-agent.conf
;;
esac
else
color ok
echo DIR OK ${mydir} was successful.
fi
color reset
echo
# --- cleanup on remote target
h2 "`date` Cleanup old backup data"
echo duplicity remove-older-than $STORAGE_KEEP --force ${sParams} ${sTarget}
color cmd
duplicity remove-older-than $STORAGE_KEEP --force ${sParams} ${sTarget}
fetchrc
color reset
echo
echo
else
color error
echo "DIR SKIP $mydir ... does not exist (no error)"
color reset
fi
echo
done
echo $rc > ${rcfile}
exit $rc
) | tee -a $transferlog
# rc=${PIPESTATUS[0]}
rc=`cat ${rcfile}`
rm -f "${lockfile}" "${rcfile}"
j_transferEnd
echo STATUS $0 exit with final returncode rc=$rc | tee -a $transferlog
echo | tee -a $transferlog
if [ $rc -eq 0 ]; then
color ok
echo Backup OK | tee -a $transferlog
else
color error
echo Backup FAILED :-/ | tee -a $transferlog
fi
color reset
echo | tee -a $transferlog
echo `date` TRANSFER DONE | tee -a $transferlog
ls -l $transferlog
exit $rc
# --------------------------------------------------------------------------------
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment