The way to do this -- indeed, the way to do many things
with the Korn shell -- is with a looping construct.
The simplest and most widely
applicable of the shell's looping constructs is the
for loop.
We'll use for to enhance fileinfo soon.
The for loop allows you to repeat a section of code a fixed
number of times. During each time through the code (known as an
iteration), a special variable called a loop variable is
set to a different value; this way each iteration can do something
slightly different.
$ first="I am first" Initialize test variables
$ second="I am in the middle"
$ third="I am last"
$ nameref refvar=first Create nameref
$ for refvar in first second third ; do Loop over variables
> print "refvar -> ${!refvar}, value: $refvar" Print referenced var, value
> done
refvar -> first, value: I am first
refvar -> second, value: I am in the middle
refvar -> third, value: I am last
$ print ${!refvar}, $refvar Show final state
third, I am last
[motet.early.com]
Trying 127.146.63.17...
-User- -Full name- -What- Idle TTY -Console Location-
hildy Hildegard von Bingen ksh 2d5h p1 jem.cal (Telnet)
mikes Michael Schultheiss csh 1:21 r4 ncd2.cal (X display 0)
orlando Orlando di Lasso csh 28 r7 maccala (Telnet)
marin Marin Marais mush 1:02 pb mussell.cal (Telnet)
johnd John Dowland tcsh 17 p0 nugget.west.nobis. (X Window)
In this output, motet.early.com is the full network name of the remote
machine.
Assume the systems in your network are called fred,
bob, dave, and pete.
Then the following code would do the trick:
for sys in fred bob dave pete
do
finger @$sys
print
done
This works no matter which system you are currently logged into.
It prints output for each machine similar to the above, with blank
lines in between.
A slightly better solution would be to store the names of the systems
in an environment variable.
This way, if systems are added to your network and you
need a list of their names in more than one script, you need change
them in only one place. If a variable's value is several words separated
by spaces (or TABS), for will treat it as a list of words.
Here is the improved solution. First, put lines in your
.profile or environment file
that define the variable SYSNAMES and make
it an environment variable:
SYSNAMES="fred bob dave pete"
export SYSNAMES
Then, the script can look like this:
for sys in $SYSNAMES
do
finger @$sys
print
done
The complete script consists of the for loop code and
the above function.
Because the function must be defined
before it can be used,
the function definition must go first,
or else it should be in a directory listed in both
PATH and FPATH.
The fileinfo script works as follows: in the for statement,
"$@" is a list of all positional parameters.
For each argument, the body of the loop is run
with filename set to that argument. In other words,
the function fileinfo is called once for
each value of $filename as its first argument ($1).
The call to print after the call to fileinfo merely
prints a blank line between sets of information about each file.
Given a directory with the same files as the previous example,
typing fileinfo * would produce the following output:
bob is a regular file.
you own the file.
you have read permission on the file.
you have write permission on the file.
you have execute permission on the file.
custom.tbl is a regular file.
you own the file.
you have read permission on the file.
you have write permission on the file.
exp is a directory that you may search.
you own the file.
you have read permission on the file.
you have write permission on the file.
lpst is a regular file.
you do not own the file.
you have read permission on the file.
That takes care of the first step in the translation process.
We can use a Korn shell string operator to handle the second.
Here is the code for a script we'll call dosmv:
for filename in ${1:+$1/}* ; do
newfilename=$(print $filename | tr '[A-Z]' '[a-z]')
newfilename=${newfilename%.}
print "$filename -> $newfilename"
mv $filename $newfilename
done
The * in the for construct is not the same
as $*. It's a wildcard, i.e., all files in a directory.
This script accepts a directory name as argument,
the default being the current directory.
The expression ${1:+$1/} evaluates to the
argument ($1) with a slash appended if the argument is
supplied, or the null string if it isn't supplied.
So the entire expression ${1:+$1/}* evaluates
to all files in the given directory, or all files in
the current directory if no argument is given.
There is one little problem with this solution: if there are any files in
the given directory that aren't MS-DOS files (in particular,
if there are files whose names don't contain uppercase letters or
don't contain a dot), then the conversion will do nothing to those
filenames and mv will be called with two identical arguments.
mv will complain with the message:
mv: filename and
filename are identical.
The solution is very simple: test to see if the filenames are identical:
for filename in ${1:+$1/}* ; do
newfilename=$(print $filename | tr '[A-Z]' '[a-z]')
newfilename=${newfilename%.}
# subtlety: quote value of $newfilename to do string comparison,
# not regular expression match
if [[ $filename != "$newfilename" ]]; then
print "$filename -> $newfilename"
mv $filename $newfilename
fi
done