[comp.unix.questions] C-shell programming

reiner@jabberwock.shs.ohio-state.edu (Reiner Wilhelms) (08/03/90)

I'd like to create a c shell program which reads from 3 files and
generates 1 (or more ) output file(s). 
Calling this csh script will look like this:

	% my_shell_script input1 input2 input3  output

The trouble is, I only know how to read from one file, using the input
redirection. I want to read from within that shell script instead of
forking a new process, or writing a C-program.

Can this be done??

I am working on a sun workstation, SunOS 4.0.3c

Thanks in advance.

Reiner

tif@doorstop.austin.ibm.com (Paul Chamberlain) (08/04/90)

In article <230@jabberwock.shs.ohio-state.edu> reiner@jabberwock.shs.ohio-state.edu (Reiner Wilhelms) writes:
>I'd like to create a c shell program which reads from 3 files and
>generates 1 (or more ) output file(s). 
>Calling this csh script will look like this:
>	% my_shell_script input1 input2 input3  output

A bourne shell script could use the 3< 4< etc. syntax like this

	:
	(
	read from_filename1 <&3
	read from_filename2 <&4
	read from_filename3 <&5
	echo to_filename_4 >&6
	) 3< $1 4< $2 5< $3 6> $4

I haven't tried this but it's at least close.  I don't think
csh has an equivalent feature.  I think you can use "{" and "}"
instead of "(" and ")" to be more efficient (one less process).

Paul Chamberlain | I do NOT represent IBM         tif@doorstop, sc30661@ausvm6
512/838-7008     | ...!cs.utexas.edu!ibmaus!auschs!doorstop.austin.ibm.com!tif

reiner@slithy-tove.shs.ohio-state.edu (Reiner Wilhelms) (08/08/90)

Last week I asked for advise to write a C-shell script which
is capable of reading from more than one file in parallel.
I was lucky: two people (at least) came up with immediate 
answers. Thank you.

Jeffrey Youngstrom (teda!jeffy@decwrl.dec.com ) had this 
piece of code designed for reading one file:
#!/bin/csh -f
# argument 1 ($1) is the input file name
@ line=1
set length=`wc -l $1 | awk '{print $1}'`  # find the number of lines
					  # in the input file
while ($line < $length)
	set stuff=`sed -n $line,${line}p $1`
	# do some stuff
	echo $stuff
	# increment the line count
	@ line++
end
exit

According to him, it is truely slow ("# disgustingly slow csh
cat..."), and he is right. However, I am still very happy that someone
found at least this solution, and it is useful because it can easily be 
extended to reading from several files in the same while loop, see below.

Paul Chamberlain (tif@doorstop.austin.ibm.com)'s important hint 
is for Bourne shells, for which it is also not really obvious
how to read several files in parallel. 

He wrote:
> A bourne shell script could use the 3< 4< etc. syntax like this
> 
>        :
>        (
>        read from_filename1 <&3
>        read from_filename2 <&4
>        read from_filename3 <&5
>        echo to_filename_4 >&6
>        ) 3< $1 4< $2 5< $3 6> $4
>
> I haven't tried this but it's at least close. I don't think
> csh has an equivalent feature...."
>

I agree, as far as I know, there is no real equivalent method
in C-shells. 

With these two hints at hand I tinkered around and finally
arrived at the following two shells which (at least for me)
work. 
The files NN.dat1, NN.dat2, and NN.dat3 contain the lines
printed below. We want to join them into the file 
NN.out, which is also printed below.

::::::::::::::
NN.dat1
::::::::::::::
Joe      32    W 
Terry    40    V
Michel   32    M
Ute      28    K
Reiner   37    G
Uwe      32    D
::::::::::::::
NN.dat2
::::::::::::::
Joe      Chicago  Terry
Terry    Cairo    Michel
Michel   Haiti    Ute
Ute      Berlin   Reiner
Reiner   Columbus Uwe
Uwe      Berlin   NIL
::::::::::::::
NN.dat3
::::::::::::::
Joe      167   ++
Terry    180   *
Michel   177   !!
Ute      180   ??
Reiner   179   +-
Uwe      178   @
::::::::::::::
NN.out (output file)
::::::::::::::
Joe/32/W/Chicago.Terry.167.JJ
Terry/40/V/Cairo.Michel.180.TM
Michel/32/M/Haiti.Ute.177.MT
Ute/28/K/Berlin.Reiner.180.UW
Reiner/37/G/Columbus.Uwe.179.REW
Uwe/32/D/Berlin.NIL.178.UWE
::::::::::::::


Each script is called via 

	% join_shell NN.dat1 NN.dat2 NN.dat3 NN.out

The Bourne-script which does the job is this:

#!/bin/sh
rm -f $4     # remove old output file 
touch $4     # create new one
(
i=0
while read name age code <&3
do
        read namex location follow <&4
        read namex size symbol <&5
        echo $name/$age/$code/$location.$follow.$size $symbol >&6
        i=`expr $i + 1`
done
echo $i lines read
) 3<$1 4<$2 5<$3 6>$4
exit


.. and the csh script which does the same job is here:

#!/bin/csh -f
@ line=1
rm -f $4   # remove old output file
touch $4   # create a new one
set length=`wc -l $1 | awk '{print $1}'`
while ($line <= $length)
        set S=`sed -n ${line}p $1`
        set W=`sed -n ${line}p $2`
        set U=`sed -n ${line}p $3`
        echo $S[1]/$S[2]/$S[3]/$W[2].$W[3].$U[2].$U[3] >> $4
        @ line++
end
echo $length lines read
exit

Both are pretty slow. But I'm glad that they at least work. 
It looks simple, doesn't it. But it takes so much time 
to figure it out, that's why I still hate shell programming.

Reiner

peter@ficc.ferranti.com (peter da silva) (08/10/90)

(a) The Bourne shell is a *much* better programming tool. Why are you using
    the C shell for scripts?

(b) The "join" command could be used to make a pipeline out of this.

#!/bin/sh
# If not already sorted:
for i in 1 2 3
do
sort -d NN.dat$i > NN.sort$i
done

join -j 1 -o 1.1 1.2 1.3 2.2 2.3 NN.sort1 NN.sort2 |
  join -j 1 -o 1.1 1.2 1.3 1.4 1.5 2.2 2.3 - NN.sort3 |
  awk '{print $1"/"$2"/"$3"/"$4"."$5"."$6" "$7}'

Appropriate use of /dev/fd could make it even neater, if you don't already
have sorted files.
-- 
Peter da Silva.   `-_-'
+1 713 274 5180.   'U`
<peter@ficc.ferranti.com>

guy@auspex.auspex.com (Guy Harris) (08/11/90)

>Appropriate use of /dev/fd could make it even neater, if you don't already
>have sorted files.

And if you *do* have "/dev/fd", which most UNIX systems at this point
don't have, although I think S5R4 might have it.