1#! /bin/bash 2# 3# original from: 4# 5# @(#) frcp.ksh 2.2 93/11/14 6# 92/06/29 john h. dubois iii (john@armory.com) 7# 92/10/14 Cleaned up, improved, added -d and -r options 8# 92/11/11 Made work with a dest of '.' 9# 93/07/09 Added -l and -n options, & login as anonymous if no .netrc entry 10# 93/11/14 Use either passwd or password in .netrc, since ftp does. 11# 12# conversion to bash v2 syntax by Chet Ramey 13# 14# frcp: ftp front end with rcp-like syntax. 15# Note: requires any machine names given to be listed with 16# user and password in .netrc. If not, anonymous FTP is 17# done. 18# 19# full path to ftp binary 20if [ -x /usr/bin/ftp ]; then 21 FTP=/usr/bin/ftp; 22elif [ -x /usr/ucb/ftp ]; then 23 FTP=/usr/ucb/ftp 24else 25 FTP=ftp 26fi 27 28istrue() 29{ 30 test 0 -ne "$1" 31} 32isfalse() 33{ 34 test 0 -eq "$1" 35} 36 37# For each filename given, put the filename in filename[n] 38# and the machine it is on in machine[n]. 39function SplitNames { 40 typeset file 41 typeset -i i=1 42 43 unset filename[*] machine[*] 44 for file; do 45 case "$file" in 46 *:*) machine[i]=${file%%:*} ;; 47 *) machine[i]=$LocalMach ;; 48 esac 49 filename[i]=${file#*:} 50 let i+=1 51 done 52} 53 54function verboseprint { 55 echo "$@" 56 echo "$@" 1>&2 57} 58 59function MakeDir { 60 OFS=$IFS 61 local IFS=/ dir component 62 63 case "$1" in 64 /*) ;; 65 *) dir=. 66 esac 67 set -- $1 68 IFS=$OFS 69 for component; do 70 dir=$dir/$component 71 if [ ! -d "$dir" ]; then 72 if mkdir "$dir"; then :; else 73 echo "Could not make directory $dir." >&2 74 return 1 75 fi 76 fi 77 done 78 return 0 79} 80 81lastisdot () 82{ 83 case "$1" in 84 */.|*/..) return 0;; 85 *) return 1;; 86 esac 87} 88 89# CopyFiles: issue ftp(TC) commands to copy files. 90# Usage: CopyFiles [sourcemachine:]sourcepath ... [destmachine:]destpath 91# Global vars: 92# Uses LocalMach (should be name of local machine) 93# Sets global arrs machine[]/filename[] 94function CopyFiles { 95 unset machine[*] filename[*] 96 97 SplitNames "$@" # split names into filename[1..n] and machine[1..n] 98 99 local DestMach=${machine[$#]} # Machine to copy files to 100 local DestPath=${filename[$#]} # Destination file/dir 101 102 unset machine[$#] filename[$#] 103 104 [ -z "$DestPath" ] && DestPath=. # dest was given as machine: 105 106 # Try to determine if destination should be a directory 107 # so that it can be forced to be a directory. 108 109 case "$DestPath" in 110 */) ;; # don't add / if trailing / already present 111 *) if [ $# -gt 2 ] || # if more than two args given, last must be a dir 112 # If dest in on local machine, check whether it is a directory 113 [ $DestMach = $LocalMach ] && [ -d "$DestPath" ] || 114 # If dest ends with . or .., it is a directory 115 lastisdot "$DestPath" 116 then 117 DestPath=$DestPath/ 118 fi ;; 119 esac 120 121 # If one of the above tests made us think dest is a directory, 122 # but it isn't, complain 123 case "$DestPath" in 124 */) if [ "$DestMach" = "$LocalMach" ] && [ ! -d "$DestPath" ]; then 125 echo "Destination is not a directory." 1>&2 126 exit 1 127 fi ;; 128 esac 129 130 DoCopy "$DestMach" "$DestPath" 131} 132 133# Usage: OpenMachine machine-name 134# Emits login sequence or doesn't, depending on .netrc file and global 135# variables anon and noanon 136OpenMachine () 137{ 138 local machine=$1 netrc=$HOME/.netrc user= password= 139 140 if isfalse $anon && [ -r $netrc ]; then 141 set -- $(gawk ' 142 /machine (.* )?'"$machine"'($| )/,/^ *$/ { 143 Fields[$1] = $2 144 if ("passwd" in Fields) 145 Fields["password"] = Fields["passwd"] 146 if ("login" in Fields && "password" in Fields) { 147 print Fields["login"] " " Fields["password"] 148 exit 149 } 150 } 151 ' $netrc ) 152 user=$1 153 password=$2 154 fi 155 if [ -z "$password" ]; then 156 if istrue $noanon; then 157 echo "No .netrc entry for machine $machine" 1>&2 158 exit 1 159 fi 160 user=anonymous 161 password=$USER@$LocalMach 162 fi 163 verboseprint open $machine 164 echo user $user "*******" 1>&2 165 echo user $user $password 166} 167 168# Usage: DoCopy destination-machine destination-path 169# Copies the files in global arrs machine[]/filename[] to the given dest 170# Global vars: 171# Uses machine[], filename[], LocalMach, check 172DoCopy () 173{ 174 local DestMach=$1 175 local DestPath=$2 176 local OpenMach # Machine that connection is currently open to 177 local OWD=$PWD SourceMach SourceFile 178 local FileName 179 typeset -i i=1 180 181 while [ $i -le ${#machine[*]} ]; do 182 istrue $check && verboseprint "runique" 183 184 SourceMach=${machine[i]} 185 SourceFile=${filename[i]} 186 187 DestFile=$DestPath 188 # if DestPath is a dir, 189 # add source filename to it without source path 190 case "$DestFile" in 191 */) DestFile=$DestFile${SourceFile##*/} ;; 192 esac 193 194 if [ $SourceMach = $LocalMach ]; then 195 if [ $DestMach != "$OpenMach" ]; then 196 OpenMachine $DestMach 197 OpenMach=$DestMach 198 fi 199 verboseprint put $SourceFile $DestFile 200 elif [ $DestMach = $LocalMach ]; then 201 if istrue $check && [ -f "$DestFile" ]; then 202 echo "$DestFile already exists." 1>&2 203 continue 204 fi 205 # If destination is on local machine, 206 # the dest will be a full dir/filename 207 if istrue $createdirs; then 208 MakeDir "${DestFile%/*}" || continue 209 fi 210 if [ $SourceMach != "$OpenMach" ]; then 211 OpenMachine $SourceMach 212 OpenMach=$SourceMach 213 fi 214 # If source filename has wildcards ([, ], *, ?) do an mget 215 case "$SourceFile" in 216 \[*\]|*\**|*\?*) 217 verboseprint lcd "$DestFile" 218 verboseprint mget "$SourceFile" 219 verboseprint lcd $OWD ;; 220 *) verboseprint get "$SourceFile" "$DestFile" ;; 221 esac 222 else 223 echo "Neither source machine \"$SourceMach\" "\ 224"nor destination machine \"$DestMach\" is local." 1>&2 225 fi 226 let i+=1 227 done 228} 229 230# Start of main program 231name=${0##*/} 232 233if [ "$1" = -h ]; then 234 echo \ 235"$name: do ftp transfers using rcp-style parameters. 236Usage: $name <source> <destpath> or $name <source> [<source> ...] <destdir> 237At least one of <source> and <destpath> must be the local system. 238A remote filename is given as machinename:filename 239If remote filenames contain wildcards, they will be globbed on the remote 240machine. Make sure they are quoted when $name is invoked. 241If the invoking user's .netrc file (see ftp(TC)) contains an entry for the 242remote system with a login and password supplied, $name will log in using 243the given login and password. If not, $name will login in as user 244anonymous and with the user@localsystem as the password. 245Options: 246-c: check: do not overwrite files. 247-d: create directories as needed. 248-f: force: overwrite files (default). 249-h: print this help. 250-l: fail if there is no entry with login and password for the remote system, 251 instead of logging in as anonymous. 252-n: log in as anonymous even if there is an entry for the remote system in 253 the user's .netrc file. 254-r: read source/dest filename pairs from the standard input, 255 one pair per line, and copy files accordingly." 256 exit 0 257fi 258 259typeset -i check=0 createdirs=0 readinput=0 anon=0 noanon=0 260 261while getopts :cdflnr Option 262do 263 case "$Option" in 264 c) check=1;; 265 d) createdirs=1;; 266 f) check=0;; 267 l) noanon=1;; 268 n) anon=1;; 269 r) readinput=1;; 270 \?) echo "$OPTARG: invalid option."; exit 1;; 271 esac 272done 273 274shift $((OPTIND-1)) 275 276LocalMach=`hostname` 277 278if istrue $readinput; then 279 while read line; do 280 CopyFiles $line 281 done | $FTP -nv 282else 283 if [ $# -lt 2 ]; then 284 echo "$name: Not enough arguments. Use -h for help." 1>&2 285 exit 286 fi 287 CopyFiles "$@" | $FTP -nv 288fi 289