world% ncftp ftp.sopwith.com:/pub/pilots/snoopy.zip(In the example scripts, we'll explicitly call what is, at this writing, the newest version of ncftp, ncftp-2.3.0, which works "cleanly" if the file it tries to get off a remote ftp server does not exist.)
world% pico getsnoopyThen, in the file, put in the command you want to run:
ncftp ftp.sopwith.com:/pub/pilots/snoopy.zipthen save and exit, and make the file executable with this command:
world% chmod +x getsnoopyNow, you can just run the command with
world% getsnoopy
#!/bin/shAt the very beginning of every shell script. (The # has to be the first character in the file, there can't be even a blank line before it.) Now, when you run a script, it will first fire up a program named "sh", the bourne shell. The bourne shell will read and execute the rest of the commands in the file.
#!/bin/sh # get that snoopy file from the sopwith server ncftp-2.3.0 ftp.sopwith.com:/pub/pilots/snoopy.zip
Now, let's change the script to check on more than one server. This illustrates using variables. In shell, a variable assignment looks like:
SERVER=ftp.volkswagen.comTo get a use a the value of a variable in a script, you write "$NAME", substituting the name of the variable for "NAME". (Variable names don't have to be all uppercase, but they traditionally are. Since commands are usually given all lowercase names, the variable names stand out. So, anywhere in a script we write "$SERVER", we'll get "ftp.volkswagen.com". Besides saving typing, that's not interesting, unless we have some way to make the contents of the variable change. The "for/in/do/done" construct is one such method.
If we write:
for $SERVER in ftp.washington.edu ftp.volkswagen.com ftp.ftp.com do some-commands-that-use-the SERVER variable doneThe commands between "do" and "done" will be executed repeatedly, with "SERVER" set to each of the three server names. Now, a complication comes up. We want to use a variable's value inside a larger string. If we just wanted our script to run a command like:
ncftp ftp.washington.eduWe'd just say "ncftp $SERVER". Instead, we want something like:
ncftp ftp.washington.edu/pub/pilots/snoopy.eduIn such cases, we put the name of the variable, but not the $, in curly braces. sh (the program executing the script), knows that that means "the variable name starts and stops here". (Actually, unless the variable name is immediately adjacent to a character that could be part of a variable name, the curly braces are not necessary. Letters, digits, and underscores are the only characters that can appear in variable names.) E.g.,
ncftp ${SERVER}/pub/pilots/snoopy.edu
So, here's our script
#!/bin/sh
#try to get snoopy off a few file servers
#first, set a variable to the name of the file we want to get
FILENAME=snoopy.zip
for SERVER in ftp.washington.edu ftp.volkswagen.com ftp.ftp.com
do
ncftp-2.3.0 ${SERVER}:/pub/pilots/${FILENAME}
mv $FILENAME snoopy.from.${SERVER}.zip
done
That script will contact each of the servers in the list and
snag the file. The mv command will rename the file after it's
been gotten, so the one we get from the next server won't
clobber it. The general syntax of "for" is:
for VARNAME in word1 word2 word3 word4 do somecommands done"somecommands" will almost always reference the variable "$VARNAME" in practice (else why set it?), but that's not syntactically imposed. An example of a for loop:
for COLOR in red blue green do rm code-$COLOR doneThat loop would try to remove three files, "code-red", "code-blue", and "code-green".
for SERVER in ftp.washington.edu ftp.volkswagen.com ftp.ftp.com
do
echo about to get $FILENAME from $SERVER
ncftp-2.3.0 ${SERVER}:/pub/pilots/${FILENAME}
mv $FILENAME snoopy.from.${SERVER}.zip
done
if [ -f $FILENAME ]
then
mv $FILENAME snoopy.from.${SERVER}.zip
fi
"fi" isn't a typo, it's "if" spelled backward, and ends an "if
block". The text between "if" and "fi" will only be executed if
the file named as the contents of the variable FILENAME exists.
Any programmer unfamiliar with shell would assume that the
notation: "[ -f something]" was part of shell's syntax. That's
not the case. In fact, "[" is the name of a program, which
is also called test. You usually
see "if" followed by "[...something...]", but that's not
necessarily the case. "test", a.k.a "[", is one of several
mini-languages shell programmers learn, but not technically a
part of the shell.
if expression then some commands else some other commands fi
In the case of our script, we want to tell the user if the file doesn't exist. So, we can re-write the "if" part of the script to look like
#!/bin/sh
#try to get snoopy off a few file servers
#first, set a variable to the name of the file we want to get
FILENAME=snoopy.zip
for SERVER in ftp.washington.edu ftp.volkswagen.com ftp.ftp.com
do
ncftp-2.3.0 ${SERVER}:/pub/pilots/${FILENAME}
# did we successfully get the file?
if [ -f $FILENAME ]
then
# we got it! rename it and tell the user
mv $FILENAME snoopy.from.${SERVER}.zip
echo got it from $SERVER, saved as snoopy.from.${SERVER}.zip
else
# oops, file doesn't exist, tell the user
echo could not get $FILENAME from $SERVER
fi
done
echo "Do you really want to erase ${FILENAME}?"
read ANSWER
That will stop to read an answer from the user, and put whatever the user types into the variable ANSWER. (You can use 'expr', or 'grep', or several other utilities to decide whether the answer was a yes or a no.)
In our script, we're going to read server names out of a file. Most commonly, scripts read from standard input, that is, the keyboard. Here, we show one way to read from a file. The notation is:
exec 0< filenameWhat that does is change the standard input of the executing script to the named file. Subsequent commands that read standard input (including the one we're interested in here, the shell built-in "read" command) will get their input from the named file. This is not the "normal" use of the exec command, by the way -- usually, you use it to mean "abandon execution of the currently running program and launch a new program".
Using the redirection command above, we can use the shell "read" command to read the file. The syntax of read is:
read VARNAME1 VARNAME2 . . ."read" takes one line from the standard input, it assigns the first word in the line to VARNAME1, the second to VARNAME2, etc. In our case we only expect there to be one word, the name of a server, on each line.
(You can't specify another source for read, which is why we redirect standard input with exec to read from a file).
Our new script, which will get the servers to connect to from a file in our home directory named "snoopyservers", looks like this:
#!/bin/sh
#try to get snoopy off a few file servers, they're named in
#the file snoopyservers in our home directory
SERVERLIST=${HOME}/snoopyservers
#first, set a variable to the name of the file we want to get
FILENAME=snoopy.zip
if [ -f $SERVERLIST ]
then
echo "File $SERVERLIST is missing"
exit
fi
# don't need an "else" -- exit kills the script so it
# won't get here if the file doesn't exist
exec 0< $SERVERLIST
while read SERVER
do
ncftp-2.3.0 ${SERVER}:/pub/pilots/${FILENAME}
# did we successfully get the file?
if [ -f $FILENAME ]
then
# we got it! rename it and tell the user
mv $FILENAME snoopy.from.${SERVER}.zip
echo got it from $SERVER, saved as snoopy.from.${SERVER}.zip
else
# oops, file doesn't exist, tell the user
echo could not get $FILENAME from $SERVER
fi
done
You can use "break" to break out of a "while" or "for" loop. For example, if you wanted to get only one copy of snoopy.zip, you could add the "break" after the "echo got it" line:
mv $FILENAME snoopy.from.${SERVER}.zip
echo got it from $SERVER, saved as snoopy.from.${SERVER}.zip
break
Below is a final revision of the script, it makes a couple small changes. First, we make the script check for several different files. Instead of "snoopy.zip", we'll search for "ls-lR", "ls-lR.Z", and "INDEX.ZIP", all of which are common names for files which give the directory listing of files on an ftp server. (The significance of the name "ls-lR" was explained in a previous issue of "Today on The World. Those files are usually stored in the /pub directory, so that's where we'll look. (You can change the script to consider any other filename as a match by adding to the string that initializes the variable CANDIDATE_NAMES.) If the script doesn't get a file with the given name, it looks for the next name on the list. However, if it does find one, it uses "break" (described above) to get out of the "for" loop, and the script continues on to the next server named in the list of servers.
# try to get some file servers
# first, set a variable to the name of the file we want to get
CANDIDATE_NAMES="ls-lR ls-lR.Z INDEX.ZIP"
SERVERLIST=${HOME}/.lslrserverlist
for FILENAME in $CANDIDATE_NAMES
do
if [ -f $FILENAME ]
then
echo "$FILENAME already exists, terminating"
exit
fi
done
if [ ! -f $SERVERLIST ]
then
echo "$SERVERLIST doesn't exist, terminating"
exit
fi
exec 0< $SERVERLIST
while read SERVER
do
for FILENAME in $CANDIDATE_NAMES
do
ncftp-2.3.0 ${SERVER}:/pub/${FILENAME}
# did we successfully get the file?
if [ -f $FILENAME ]
then
# we got it! rename it and tell the user
NEWNAME=${FILENAME}.from.${SERVER}
mv $FILENAME $NEWNAME
echo got it from $SERVER, saved as $NEWNAME
break # break out of the for loop, and go on to next server
else
# oops, file doesn't exist, tell the user
echo could not get $FILENAME from $SERVER
fi
done
done
HOME Top of Help Desk Eye On The World The World Kiosk
For further assistance, please send an email
request to support@world.std.com
Copyright 1996 Software Tool & Die, Inc. All Rights Reserved.
Software Tool & Die, Inc., 1330 Beacon St. Suite 215 Brookline, MA 02146