Patch-ID: bash41-003
[bash.git] / examples / scripts.v2 / frcp
blob572aa7bc25f1221f3f0e9a64cc422697ef28a06a
1 #! /bin/bash
3 # original from:
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.
12 # conversion to bash v2 syntax by Chet Ramey
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.
19 # full path to ftp binary
20 if [ -x /usr/bin/ftp ]; then
21 FTP=/usr/bin/ftp;
22 elif [ -x /usr/ucb/ftp ]; then
23 FTP=/usr/ucb/ftp
24 else
25 FTP=ftp
28 istrue()
30 test 0 -ne "$1"
32 isfalse()
34 test 0 -eq "$1"
37 # For each filename given, put the filename in filename[n]
38 # and the machine it is on in machine[n].
39 function SplitNames {
40 typeset file
41 typeset -i i=1
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
54 function verboseprint {
55 echo "$@"
56 echo "$@" 1>&2
59 function MakeDir {
60 OFS=$IFS
61 local IFS=/ dir component
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
77 done
78 return 0
81 lastisdot ()
83 case "$1" in
84 */.|*/..) return 0;;
85 *) return 1;;
86 esac
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[]
94 function CopyFiles {
95 unset machine[*] filename[*]
97 SplitNames "$@" # split names into filename[1..n] and machine[1..n]
99 local DestMach=${machine[$#]} # Machine to copy files to
100 local DestPath=${filename[$#]} # Destination file/dir
102 unset machine[$#] filename[$#]
104 [ -z "$DestPath" ] && DestPath=. # dest was given as machine:
106 # Try to determine if destination should be a directory
107 # so that it can be forced to be a directory.
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
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
130 DoCopy "$DestMach" "$DestPath"
133 # Usage: OpenMachine machine-name
134 # Emits login sequence or doesn't, depending on .netrc file and global
135 # variables anon and noanon
136 OpenMachine ()
138 local machine=$1 netrc=$HOME/.netrc user= password=
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
151 ' $netrc )
152 user=$1
153 password=$2
155 if [ -z "$password" ]; then
156 if istrue $noanon; then
157 echo "No .netrc entry for machine $machine" 1>&2
158 exit 1
160 user=anonymous
161 password=$USER@$LocalMach
163 verboseprint open $machine
164 echo user $user "*******" 1>&2
165 echo user $user $password
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
172 DoCopy ()
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
181 while [ $i -le ${#machine[*]} ]; do
182 istrue $check && verboseprint "runique"
184 SourceMach=${machine[i]}
185 SourceFile=${filename[i]}
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
194 if [ $SourceMach = $LocalMach ]; then
195 if [ $DestMach != "$OpenMach" ]; then
196 OpenMachine $DestMach
197 OpenMach=$DestMach
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
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
210 if [ $SourceMach != "$OpenMach" ]; then
211 OpenMachine $SourceMach
212 OpenMach=$SourceMach
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
226 let i+=1
227 done
230 # Start of main program
231 name=${0##*/}
233 if [ "$1" = -h ]; then
234 echo \
235 "$name: do ftp transfers using rcp-style parameters.
236 Usage: $name <source> <destpath> or $name <source> [<source> ...] <destdir>
237 At least one of <source> and <destpath> must be the local system.
238 A remote filename is given as machinename:filename
239 If remote filenames contain wildcards, they will be globbed on the remote
240 machine. Make sure they are quoted when $name is invoked.
241 If the invoking user's .netrc file (see ftp(TC)) contains an entry for the
242 remote system with a login and password supplied, $name will log in using
243 the given login and password. If not, $name will login in as user
244 anonymous and with the user@localsystem as the password.
245 Options:
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
259 typeset -i check=0 createdirs=0 readinput=0 anon=0 noanon=0
261 while getopts :cdflnr Option
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
272 done
274 shift $((OPTIND-1))
276 LocalMach=`hostname`
278 if istrue $readinput; then
279 while read line; do
280 CopyFiles $line
281 done | $FTP -nv
282 else
283 if [ $# -lt 2 ]; then
284 echo "$name: Not enough arguments. Use -h for help." 1>&2
285 exit
287 CopyFiles "$@" | $FTP -nv