#! /bin/bash # batch_scp - script for automatic file/directory transfer using scp #--------------------------------------------------------------------------------# # This file is part of the PALM model system. # # PALM is free software: you can redistribute it and/or modify it under the terms # of the GNU General Public License as published by the Free Software Foundation, # either version 3 of the License, or (at your option) any later version. # # PALM is distributed in the hope that it will be useful, but WITHOUT ANY # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR # A PARTICULAR PURPOSE. See the GNU General Public License for more details. # # You should have received a copy of the GNU General Public License along with # PALM. If not, see . # # Copyright 1997-2021 Leibniz Universitaet Hannover #--------------------------------------------------------------------------------# # batch_scp - script for automatic file/directory transfers using scp # # batch_scp has up to 5 arguments (first 4 are mandatory): # $1 = IP-addres of remote (target) machine # $2 = file to be transferred # $3 = directory of remote machine, where file should be copied to # $4 = filename that file should be given on remote machine # $5 = file extension (optional argument) # # ATTENTION: problems might occur if directories on remote machine include very # old files for which "ls -al" does give "year" as modification # time instead of "hh:mm". In such a case, batch_scp cannot check the # filename and may not find the file (e.g. if option -g is used). #--------------------------------------------------------------------------------# # VARIABLE DECLARATIONS + DEFAULT VALUES random=$RANDOM absolut=false append=false catalog_copy=false check=false cyclestring="" delete=false errfile=batch_scp.errfile.$random filelist=filelist.$random get=false local_host=`hostname` local_wdir=`pwd` locat=normal make_catalog=false overwrite=false print_local_filename=false quote_wait=false remote_user="" silent=false transfermode=binary typeset -i iii icycle maxcycle=0 usecycle wait=0 # ERROR HANDLING IN CASE ... # ... EXIT trap 'if [[ $locat != normal ]] then [[ -f "$filelist" ]] && cat $filelist [[ -f "$errfile" ]] && cat $errfile rm -rf $filelist $errfile printf " +++ BATCH_SCP terminated \n" printf " locat = $locat \n" printf " arguments = $1 $2 $3 $4 \n\n" exit 1 fi' exit # ... TERMINAL-BREAK: trap 'rm -rf $filelist $errfile printf " +++ BATCH_SCP terminated \n\n" exit 1 ' 2 # READ SHELLSCRIPT-OPTIONS while getopts :aAbcCdgmnoP:qsu:U:w: option do case $option in (a) absolut=true;; (A) append=true;; (b) transfermode=binary;; (c) catalog_copy=true;; (C) check=true;; (d) delete=true;; (g) get=true;; (m) make_catalog=true;; (n) print_local_filename=true;; # Option ist nicht dokumentiert ! (o) overwrite=true;; (P) scp_port=$OPTARG;; (q) quote_wait=true;; (s) silent=true;; (u) remote_user=$OPTARG;; (U) usecycle=$OPTARG;; (w) wait=$OPTARG;; (\?) printf " +++ option $OPTARG unknown \n" printf " --> call: batch_scp [-aAbcCdgmnoqsuw] \n" locat=parameter;exit;; esac done (( to_shift = $OPTIND - 1 )) shift $to_shift # LIST SHORT DESCRIPTION OF AVAILABLE OPTIONS if [ "$1" = "?" ] then (printf "\n *** batch_scp can be called as follows:\n" printf "\n batch_scp -a -b -d -g -o -q -s -u.. -U.. -w.. \n" printf "\n Description of available options:\n" printf "\n Option Description Default-Value" printf "\n -a Filenames are absolute. No cycle- ---" printf "\n numbers will be determined" printf "\n -A append to destination file ---" printf "\n -b use binary-modus for transfer ASCII-modus" printf "\n -c transfer of directory ---" printf "\n -C check-Modus, no transfer ---" printf "\n -d file to be transferred will be ---" printf "\n deleted after successful transfer" printf "\n -g change of transfer direction, i.e. ---" printf "\n file will be transferred from" printf "\n destination host" printf "\n -o any existing file will be overwritten ---" printf "\n -q switch on \"quote wait\" on ---" printf "\n estination host" printf "\n -s do not display informative messages ---" printf "\n -u username on remote machine " printf "\n -U cycle number to be used ---" printf "\n -w waiting time in seconds, before trans- 0" printf "\n fer will be initiated" printf "\n " printf "\n The positional parameters - must be provided at" printf "\n any time and have the following meaning:" printf "\n - IP-adress of destination host" printf "\n or \"?\" (creates this short summary of options)" printf "\n - abs. or rel. path of file to be transferred" printf "\n - directory (abs.!) on destination host. Special cahracters" printf "\n like \~ are allowed but must be quoted by \"." printf "\n - filename (without path!) on destination host; must not" printf "\n be given, if option -c is used." printf "\n When using option -g, file will be copied from destination host to file" printf "\n . In this case, no overwriting is possible.") | more exit fi # CHECK FOR COMPLETENESS OF ARGUMENTS if [[ "$1" = "" ]] then printf " +++ 1. argument missing \n" locat=argument; exit elif [[ "$2" = "" ]] then printf " +++ 2. argument missing \n" locat=argument; exit elif [[ "$3" = "" ]] then printf " +++ 3. argument missing \n" locat=argument; exit elif [[ "$4" = "" ]] then printf " +++ 4. argument missing \n" locat=argument; exit fi # USER-NAME AUF ZIELRECHNER AUS .NETRC-DATEI ERMITTELN if [[ -z $remote_user ]] then printf " +++ option -u is missing \n" locat=remote_user; exit fi # APPEND IS ONLY ALLOWED FOR TRANSFER OF SINGLE FILES WITHOUT OVERWRITING # IN SUCH A CASE GET IS NOT ALLOWED TOO if [[ $append = true && ( $get = true || $catalog_copy = true || $overwrite = true ) ]] then printf " +++ options -g, -c and -o are not allowed, if -A is given \n" locat=parameter; exit fi # QUOTE WAIT DOES NOT WORK IF COMPLETE FOLDERS ARE COPIED if [[ $quote_wait = true && $catalog_copy = true ]] then printf " +++ options -c and -q must not be used simultaneously\n" locat=parameter; exit fi # SCRIPT WILL BE ENDED HERE IN CASE OF CHECK-MODE [[ $check = true ]] && exit # WAIT A BIT (MAY BE REQUIRED IN CASE OF TRANSFERS OF JOB PROTOCOLS FROM # WITHIN A JOB) sleep $wait # SET PORT NUMBER OPTION FOR CALLS OF SSH/SCP if [[ "$scp_port" != "" ]] then PORTOPT="-P $scp_port" SSH_PORTOPT="-p $scp_port" fi # CHECK, IF LOCAL FILE/FOLDER EXISTS if [[ $get = false ]] then if [[ $catalog_copy = false ]] then if [[ ! -f $2 ]] then printf " +++ file \"$2\" to be transferred does not exist \n" locat=localfile; exit fi else if [[ ! -d $2 ]] then printf " +++ directory \"$2\" to be transferred does not exist\n" printf " or is not a directory \n" locat=localfile; exit fi fi else if [[ $catalog_copy = false ]] then if [[ -f $2 ]] then if [[ $overwrite = true ]] then rm $2 else printf " +++ local file \"$2\" is already existing \n" locat=localfile; exit fi else # CHECK, IF LOCAL FILE CAN BE CREATED local_dirname=`dirname $2` if [[ ! -d $local_dirname ]] then printf " +++ local directory \"$local_dirname\" \n" printf " does not exist or is not a directory \n" printf " +++ cannot copy file \"$3/$4\" \n" printf " from \"$1\" to \"$local_host\" \n" locat=localfile; exit fi fi else if [[ -d $2 || -f $2 ]] then printf " +++ local directory \"$2\" is already existing, \n" printf " or a file with the same name exists \n" locat=localfile; exit fi fi fi # CREATE FILE LIST OF THE TARGET HOST FOLDER ssh $SSH_PORTOPT $1 -l $remote_user "unset LANG; cd $3; ls -1; echo '*** list complete'" > $filelist 2>&1 ssh_status=$? if [[ $ssh_status != 0 ]] then if [[ ! -f $filelist ]] then echo " local_host = $local_host ssh_status = $ssh_status" locat=ssh_failed_1; exit else if [[ $(grep -c "*** list complete" $filelist) = 0 ]] then echo " local_host = $local_host ssh_status = $ssh_status" locat=ssh_failed_2; exit fi fi fi # CHECK, IF FOLDER EXISTS. A FOLDER MUST NOT EXIST, IF COMPLETE FOLDERS # SHALL BE COPIED TO THE TARGET HOST if [[ $(cat $filelist | grep -c "not found") != 0 || \ $(cat $filelist | grep -c "No such file or directory") != 0 ]] then if [[ ! ( $catalog_copy = true && $get = false ) ]] then if [[ $make_catalog = false ]] then printf " +++ directory \"$3\" does not exist on destination host (\"$1\") \n" locat=directory; exit else if [[ $silent = false ]] then printf " >>> directory \"$3\" does not exist on destination host (\"$1\")" printf "\n trying to create \"$3\" \n" fi make_catalog=force fi fi fi # CHECK, IF FILE/FOLDER EXISTS. IF SO, DETERMINE HIGHEST CYCLE NUMBER # (OR CHECK, IN ABSOLUT-MODE, IF FILE EXSITS) # BUT DO THIS IN NON-OVERWRITE-MODE ONLY found=false if [[ ( $overwrite = false && $get = false ) || $get = true ]] then while read line do if [[ $absolut = false ]] then # REMOVE EXTENSION, IF EXISTING AND GIVEN AS ARGUMENT if [[ "$5" != "" && "$5" != " " ]] then extension=${line##*.} if [[ $extension = $5 ]] then text=${line%.*} else text=${line} fi else text=${line} fi # GET AND REMOVE CYCLE NUMBER, IF EXISTING, AND CHECK, IF FILE EXISTS cycle=${text##*.} if [[ $cycle = $text ]] then # filename contains no dot, i.e. no cycle number if [[ "$text" = "$4" ]] then found=true (( icycle = 0 )) fi else # filename contains at least one dot # find out if the string after the last dot is a number if [[ $cycle =~ ^-?[0-9]+$ ]] then text=${text%.*} if [[ "$text" = "$4" ]] then found=true (( icycle = $((10#$cycle)) )) fi else if [[ "$text" = "$4" ]] then found=true (( icycle = 0 )) fi fi fi if (( icycle > maxcycle )) then (( maxcycle = icycle )) fi else # ABSOLUT-MODE ONLY REQUIRES TO CHECK IF FILE EXISTS [[ $4 = $line ]] && found=true fi done <$filelist fi if [[ $found = true ]] then if [[ $get = false ]] then if [[ $absolut = false ]] then if [[ $append = false ]] then (( maxcycle = maxcycle + 1 )) # TRY TO USE FIXED CYCLE NUMBER, IF GIVEN AS OPTION if [[ "$usecycle" != "" ]] then if (( usecycle >= maxcycle )) then (( maxcycle = usecycle )) else printf " >>> Unified cycle number cannot be used\n" fi fi fi cyclestring=`printf ".%03d" $maxcycle` else if [[ $overwrite = false ]] then printf " +++ file \"$3/$4\" \n" printf " already exists on destination host (use -o, if necessary) \n" locat=file; exit fi fi else if [[ $absolut = false ]] then # MAKE CYCLE NUMBER THREE DIGITS WIDE cyclestring=`printf ".%03d" $maxcycle` else cyclestring="" fi fi else if [[ "$usecycle" != "" ]] then (( maxcycle = usecycle )) fi cyclestring=`printf ".%03d" $maxcycle` # EXIT, IF FILE SHALL BE GET FROM THE TARGET HOST, BUT DOESN'T EXIST if [[ $get = true ]] then printf " +++ file \"$3/$4\" \n" printf " does not exist on destination host (\"$1\") \n" locat=remotefile; exit fi fi # IF NAME-OPTION (-n) IS CHOSEN, ONLY DETERMINE THE LOCAL FILENAME ON THE # TARGET HOST AND EXIT THE SCRIPT if [[ $print_local_filename = true ]] then printf "$4$cyclestring\n" rm -r $filelist exit fi # IF A 5. ARGUMENT IS GIVEN, IT WILL BE PUT AS FILENAME EXTENSION/APPENDIX # AFTER THE CYCLE NUMBER (ONLY WORKS IN CASE OF FILE COPY TO THE TARGET HOST) if [[ "$5" != "" && $get = false ]] then cyclestring=${cyclestring}.$5 fi # IN CASE OF FOLDER TRANSER TO THE TARGET HOST, CHECK IF ARGUMENT $3 REALLY # REFERS TO A FOLDER ON THE TARGET HOST if [[ $catalog_copy = true && $get = true ]] then rm -rf $filelist ssh $SSH_PORTOPT $1 -l $remote_user "cd $3" > $filelist if [[ $? != 0 ]] then locat=ssh_failed_3; exit fi if [[ $(cat $filelist | grep -c "Not a directory") != 0 ]] then printf " +++ \"$3\" on destination host is not a directory \n" locat=directory; exit fi fi # IN CASE OF FOLDER TRANSFER FROM THE TARGET HOST TO THE LOCAL HOST, # CREATE THE RESPECTIVE FOLDER ON THE LOCAL HOST if [[ $catalog_copy = true ]] then if [[ $get = true ]] then mkdir $2 fi fi catalog_name=$3 [[ "$catalog_name" != "" ]] && catalog_name=${catalog_name}/ # COPY FILE/FOLDER VIA SCP if [[ $get = false ]] then if [[ $make_catalog != force ]] then if [[ $append = false ]] then if [[ $catalog_copy = false ]] then scp $PORTOPT -p $2 $remote_user@$1:$catalog_name$4$cyclestring > /dev/null else scp $PORTOPT -p -r $2 $remote_user@$1:$catalog_name$4$cyclestring > /dev/null fi scp_status=$? if [[ $scp_status != 0 ]] then # CHECK, IF FILE SIZES ON LOCAL HOST AND TARGET HOST MATCH local_size=`ls -al $2` local_size=`echo $local_size | cut -d" " -f5` remote_size=`ssh $SSH_PORTOPT $1 -l $remote_user "ls -al $catalog_name$4$cyclestring"` remote_size=`echo $remote_size | cut -d" " -f5` if [[ "$remote_size" != "$local_size" ]] then echo " +++ scp failed on host \"$local_host\" with exit $scp_status" echo " local size = \"$local_size\" remote size = \"$remote_size\" " date locat=scp_failed; exit fi fi else scp $PORTOPT -p $2 $remote_user@$1:${catalog_name}batch_scp_append_file.$random > /dev/null if [[ $? != 0 ]] then # CHECK, IF FILE SIZES ON LOCAL HOST AND TARGET HOST MATCH local_size=`ls -al $2` local_size=`echo $local_size | cut -d" " -f5` remote_size=`ssh $SSH_PORTOPT $1 -l $remote_user "ls -al ${catalog_name}batch_scp_append_file.$random"` remote_size=`echo $remote_size | cut -d" " -f5` if [[ "$remote_size" != "$local_size" ]] then echo " +++ scp failed on host \"$local_host\" with exit $scp_status" echo " local size = \"$local_size\" remote size = \"$remote_size\" " date locat=scp_for_append_failed; exit fi fi rm $filelist ssh $SSH_PORTOPT $1 -l $remote_user "cd $3; cat batch_scp_append_file.$random >> $4$cyclestring; rm batch_scp_append_file.$random; echo '*** append complete'" > $filelist if [[ $? != 0 ]] then if [[ ! -f $filelist ]] then locat=append_via_ssh_failed; exit else if [[ $(grep -c "*** append complete" $filelist) = 0 ]] then locat=append_via_ssh_failed; exit fi fi fi fi else ssh $SSH_PORTOPT $1 -l $remote_user "mkdir -p $3" if [[ $? != 0 ]] then locat=ssh_failed_4; exit fi scp $PORTOPT -p $2 $remote_user@$1:$catalog_name$4$cyclestring > /dev/null if [[ $? != 0 ]] then locat=scp_failed; exit fi fi else if [[ $catalog_copy = false ]] then if [[ $quote_wait = true ]] then printf " +++ quote wait not realized with BATCH_SCP" locat=unavailable_feature; exit else scp $PORTOPT -p $remote_user@$1:$catalog_name$4$cyclestring $2 > /dev/null if [[ $? != 0 ]] then locat=scp_failed; exit fi fi else printf " +++ get of whole cataloges not realized with BATCH_SCP so far" locat=unavailable_feature; exit fi fi # DELETE TRANSFERED FILE ON THE LOCAL HOST if [[ $delete = true && $get = false ]] then rm -rf $2 fi # FINAL MESSAGES if [[ $silent = false ]] then if (( maxcycle == 0 )) then if [[ $append = false ]] then printf " >>> transfer successful \n" else printf " >>> file was appended \n" fi else printf " >>> transfer successful \n" if [[ $append = false ]] then if [[ $catalog_copy = false ]] then printf " new file has cycle number .%03d \n" $maxcycle else printf " new catalog has cycle number .%03d \n" $maxcycle fi else printf " append to cycle number .%03d \n" $maxcycle fi fi fi rm -rf $filelist $errfile