Subject: Re: Shell script poser
Reply-To: nikki@trmphrst.demon.co.uk
Distribution: world
X-Mailer: cppnews $Revision: 1.30 $
Organization: Trumphurst Ltd.
Date: Wed, 16 Dec 1992 21:32:05 +0000

Summary of replies (long message) ...

I asked the following question ...
  | I have a file containing a list of words, one per line. The words are in
  | mixed upper and lower case (e.g. "subRoutineName").
  | 
  | I want an output file containing the same words, but with the first 
  | character of each word in upper case, and the remainder in lower case
  | (e.g. "Subroutinename").
  | 
  | Can this be done using STANDARD UNIX COMMANDS ONLY (i.e. awk, tr, sed etc.)
  | 
  | I know it can be done with a very short C program, but that isn't the 
  | point :-)
  | 
  | Please reply by mail if possible, as I don't read the comp.unix 
**| newsgroups.

[** One respondent thought this meant "I'm too busy for this, but
obviously you aren't, so use your free time to do my work for me."  
Just to explain, I link to the Internet via modem, with a long-distance 
phone call at rates U.S. citizens just wouldn't stand for ! Because of the
mechanics of my news feed, I am not in a position to just download a small
part of a group. I _have_ spent a long time testing and timing all the 
programs submitted, and making this summary. ]

I got lots of useful and interesting replies. Thankyou everyone !

All the replies that contained actual shell programs were tested on the
first 1000 words from /usr/dict/words. 

Some of them did not translate the input file at all, some gave completely
blank output, and a few others had not converted the first 10 words
within 5 minutes. All these programs were discarded. I would have spent
more time debugging them, but there were so many _good_ programs that I
didn't get round to it. 

Many of the remaining (good) programs completed in under 10 seconds. These
were all re-run on the whole of /usr/dict/words (25222 words). The results
are summarised below, including the author's name, and the main utilities 
used. Note that 5 of the programs had minor bugs

Author			Program			1000 words	25222 words
Harald Eikrem		sed			0m0.31s		0m5.50s
Mark Brader		sed			0m0.35s		0m6.18s
David J. Fiander	sed			0m0.35s		0m6.86s
Mike Taylor		sed (*1)		0m0.35s		0m7.00s
David Tamkin		ex (*2)			0m0.58s		0m7.86s
Jean-Pierre Radley	ex (*2)			0m0.70s		0m9.11s
Yong Kuen Fatt Alan	cut,paste,tr		0m0.83s		0m9.46s
Kenneth R. Kress	cut,paste,tr,sed	0m1.35s		0m12.88s
Michael J. Corrigan	sed			0m0.78s		0m14.53s
Bob Stockler(1)		nawk			0m1.70s		0m38.70s
Jim Rogers		ksh			0m1.65s		0m43.71s
Bob Stockler(2)		awk			0m2.08s		0m46.63s
Bob Stockler(3)		ksh			0m10.85s
Ken McDonell(1)		awk			0m21.86s
Ken McDonell(2)		awk			0m26.80s
Jeff Postlewaite	awk			0m36.70s
Donald Genoa		cut,tr (*3)		3m26.65s
Tim Northrup		expr			4m53.71s
Robert Katz 		expr (*3)		4m54.18s

*1 Converted the 1st character to upper case, but did not convert the
   rest to lower case.
*2 Both ed scripts inserted a blank line at the start of the file
*3 Did not convert the last character on each line to lower case.

Clearly, sed is the best tool for the job !

I include the programs submitted below. I have not included mailings which
did not contain an actual program, because I assume you are not interested
in them. I am, however, grateful to EVERYONE who replied.

From: King Ables <ables@hal.com>

for i in `cat names.txt`
do
  echo `(echo $i | cut -c1 | tr a-z A-Z)``(echo $i | cut -c2- | tr A-Z a-z)`
done

[ Processed about 3 words per second. Did not reliably convert the last 
character in each word to lower case.]

From: robert katz <rokatz@m.njit.edu>

for file
do
  while read NAME
    do
      a=`expr X$NAME : 'X\(.\).*' | tr 'a-z' 'A-Z'`
      b=`expr X$NAME : 'X.\(.*\)' | tr 'A-Z' 'a-z'`
      echo ${a}${b}
    done < $file
done

[ Processed about 3 words per second. Did not reliably convert the last 
character in each word to lower case.]

From: Brian Foster <blf@sco.com>

	awk '{ print toupper(substr($1,1,1)) tolower(substr($1,2)) }'

[ Not tried, as my awk & nawk don't have toupper.]

From: "David J. Fiander" <davidf@golem.waterloo.on.ca>
Here you go.  One sed script.

	h
	s/^.//
	y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/
	x
	s/\(.\).*/\1/
	y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/
	G
	s/\n//

From: Jim Rogers <jimr@col.hp.com>

#!/bin/ksh
typeset -Lu1 letter1
typeset -l line

while read line
do
   letter1=$line
   trim=${line#?}
   print "${letter1}${trim}"
done
exit 0

From: "Michael J. Corrigan" <corrigan@weber.ucsd.edu>

tr "[A-Z]" "[a-z]" < $file | sed -s 's/^a/A/' -e 's/^b/B/' ..... -e 's/^z/Z/'

From: "Randal L. Schwartz" <merlyn@reed.edu>

perl -p 's/.*/\u\L$&/' <infile >outfile

[ I don't have perl. ]

From:	Mark Brader <msb@sq.com>

sed '	y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/
	h
	s/.//
	x
	s/\(.\).*/\1/
	y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/
	G
	s/\n//'

From: YONG KUEN FATT ALAN <engp2120@nusunix2.nus.sg>

#!/bin/sh
tr "[A-Z]" "[a-z]" < $1 > $1.temp		# convert to lowercase
cut -c1 $1.temp > $1.temp.1			# get 1st char
tr "[a-z]" "[A-Z]" < $1.temp.1 > $1.temp.2	# convert to uppercase
cut -c2- $1.temp > $1.temp.3			# get remaining char
paste -d"\0" $1.temp.2 $1.temp.3		# paste together
rm $1.temp*					# remove temp files

From: Mike Taylor <mirk@brid.system-simulation.co.uk>

	sed '
		h
		s/^\(.\).*/\1/
		y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/
		x
		s/^.//
		x
		G
		s/\n//
	'

From: "William K. Karwin" <doublec!billk@uunet.uu.net>

tr '[A-Z]' '[a-z]' |
awk '{
    print substr($0, 1, 1);
    print substr($0, 2);
}' |
{
	while read firstchar
	do
		read remainder
		firstchar=`echo $firstchar | tr '[a-z]' '[A-Z]'`
		echo ${firstchar}${remainder}
	done
}
exit 0
[ Translated the first 28 words, then seemed to stop dead.]

From: "Kenneth R. Kress" <kkress@phent.uucp>
cat $*			| 
tr '[A-Z]' '[a-z]' 	> $$tmp
cat $$tmp		|
cut -c1			|
tr '[a-z]' '[A-Z]'	> $$tmp1
cat $$tmp		|
cut -c2-		|
paste $$tmp1 -		|
sed 's/	//'

# Clean exit
rm $$*
exit 0

From: jeff@itsesi.chi.il.us (jeff postlewaite)
if you have gawk:
{
	printf("%s",toupper(substr($0,1,1)));
	printf("%s\n",tolower(substr($0,2)));
}
[I don't have gawk.]
	
standard awk:
BEGIN {UPPER="ABCDEFGHIJKLMNOPQRSTUVWXYZ";LOWER="abcdefghijklmnopqrstuvwxyz"}
{
	for ( C = 1; C <= length($0); C++ ) {
		if (C == 1 && (LOC = index(LOWER,substr($0,C,1)))) {
			printf("%s",substr(UPPER,LOC,1));	
			}
		else if (C > 1 && (LOC = index(UPPER,substr($0,C,1)))) {
			printf("%s",substr(LOWER,LOC,1));
			}
		else {
			printf("%s",substr($0,C,1));	
			}
		}
	printf("\n");
}	

From: Ken McDonell <kenj@yarra.pyramid.com.au>

echo "using SVR4 nawk ..."
nawk <$1 \
	'{ print toupper(substr($1,1,1)) tolower(substr($1,2,length($1)-1)) }'
[My nawk doesn't support toupper.]

echo "using awk/nawk with functions ..."
nawk <$1 '
BEGIN	{ uc["a"]="A"; uc["b"]="B"; uc["c"]="C"; uc["d"]="D"; uc["e"]="E";
	  uc["f"]="F"; uc["g"]="G"; uc["h"]="H"; uc["i"]="I"; uc["j"]="J";
	  uc["k"]="K"; uc["l"]="L"; uc["m"]="M"; uc["n"]="N"; uc["o"]="O";
	  uc["p"]="P"; uc["q"]="Q"; uc["r"]="R"; uc["s"]="S"; uc["t"]="T";
	  uc["u"]="U"; uc["v"]="V"; uc["w"]="W"; uc["x"]="X"; uc["y"]="Y";
	  uc["z"]="Z";
	  lc["A"]="a"; lc["B"]="b"; lc["C"]="c"; lc["D"]="d"; lc["E"]="e";
	  lc["F"]="f"; lc["G"]="g"; lc["H"]="h"; lc["I"]="i"; lc["J"]="j";
	  lc["K"]="k"; lc["L"]="l"; lc["M"]="m"; lc["N"]="n"; lc["O"]="o";
	  lc["P"]="p"; lc["Q"]="q"; lc["R"]="r"; lc["S"]="s"; lc["T"]="t";
	  lc["U"]="u"; lc["V"]="v"; lc["W"]="w"; lc["X"]="x"; lc["Y"]="y";
	  lc["Z"]="z";
	}
function xlc(s)	{
	  res = ""
	  for (i = 1; i <= length(s); i++) {
	    c = substr(s,i,1)
	    if (c >= "A" && c <= "Z") c = lc[c]
	    res = res c
	  }
	  return res
	}
function xuc(s)	{
	  res = ""
	  for (i = 1; i <= length(s); i++) {
	    c = substr(s,i,1)
	    if (c >= "a" && c <= "z") c = uc[c]
	    res = res c
	  }
	  return res
	}
	{ print xuc(substr($1,1,1)) xlc(substr($1,2,length($1)-1)) }'

echo "using old awk ..."
awk <$1 '
BEGIN	{ uc["a"]="A"; uc["b"]="B"; uc["c"]="C"; uc["d"]="D"; uc["e"]="E";
	  uc["f"]="F"; uc["g"]="G"; uc["h"]="H"; uc["i"]="I"; uc["j"]="J";
	  uc["k"]="K"; uc["l"]="L"; uc["m"]="M"; uc["n"]="N"; uc["o"]="O";
	  uc["p"]="P"; uc["q"]="Q"; uc["r"]="R"; uc["s"]="S"; uc["t"]="T";
	  uc["u"]="U"; uc["v"]="V"; uc["w"]="W"; uc["x"]="X"; uc["y"]="Y";
	  uc["z"]="Z";
	  lc["A"]="a"; lc["B"]="b"; lc["C"]="c"; lc["D"]="d"; lc["E"]="e";
	  lc["F"]="f"; lc["G"]="g"; lc["H"]="h"; lc["I"]="i"; lc["J"]="j";
	  lc["K"]="k"; lc["L"]="l"; lc["M"]="m"; lc["N"]="n"; lc["O"]="o";
	  lc["P"]="p"; lc["Q"]="q"; lc["R"]="r"; lc["S"]="s"; lc["T"]="t";
	  lc["U"]="u"; lc["V"]="v"; lc["W"]="w"; lc["X"]="x"; lc["Y"]="y";
	  lc["Z"]="z";
	}
	{ fc = substr($1,1,1)
	  if (fc >= "a" && fc <= "z") fc = uc[fc]
	  rest = ""
	  for (i = 2; i <= length($1); i++) {
	    c = substr($1,i,1)
	    if (c >= "A" && c <= "Z") c = lc[c]
	    rest = rest c
	  }
	  print fc rest
	}'

From: "S. Spencer Sun" <spencer@phoenix.princeton.edu>
#!/usr/local/bin/perl

while (<>)
{
  chop;
  $length = length($_)-1;
  ($first, $rest) = unpack("a a$length", $_);
  $first =~ tr/a-z/A-Z/;
  $rest =~ tr/A-Z/a-z/;
  print "$first$rest\n";
}
[Ain't got perl.]

From: "Donald Genoa @ws020" <bytex!ws020!genoad@uunet.uu.net>
cat $1 | tr A-Z a-z | 
while read word
do
    first_letter=`echo $word | cut -c1 | tr a-z A-Z`
    rest_of_word=`echo $word | cut -c2-`
    echo $first_letter$rest_of_word >> $2
done

From: Bob Stockler <trebor!bob@uunet.uu.net>
:
# all within the KornShell
[ ~root = / ] || ksh $0
function UPlow {
  typeset -l   lcase    # make "lcase" lower case
  typeset -u   ucase    # make "ucase" upper case
  typeset -L40 word     # left-justify "word", length = 40
  word=$1
  ucase=${word%???????????????????????????????????????} # 39 ?'s
  lcase=${word#?}
  print $ucase$lcase
}
INFILE=/usr/dict/words
cat $INFILE |
while read ; do UPlow $REPLY ; done
exit

:
# works with any version of 'awk' that I know of . . .
INFILE=/usr/dict/words
# pipe all input through "tr" so we deal with only lower case input
<$INFILE tr "[A-Z]" "[a-z]" |
awk '
BEGIN { # build an associative array indexed by lower case letters
        # with each element containing the upper case equivalent
        for ( i=1; i<=26; i++ ) {
          ALPHA = sprintf( "%c", i + 64 )
          alpha = sprintf( "%c", i + 96 )
          CAP[alpha] = ALPHA
        }
      }
{ initial = substr( $0, 1, 1 )
  balance = substr( $0, 2    )
  print CAP[initial] balance
} '
exit

:
# works with 'nawk', 'mawk', etc. (any 'awk' with "sub()" function
INFILE=/usr/dict/words
# pipe all input through "tr" so we deal with only lower case input
<$INFILE tr "[A-Z]" "[a-z]" |
awk '
BEGIN { # build an associative array indexed by lower case letters
        # with each element containing the upper case equivalent
        for ( i=1; i<=26; i++ ) {
          ALPHA = sprintf( "%c", i + 64 )
          alpha = sprintf( "%c", i + 96 )
          CAP[alpha] = ALPHA
        }
      }
{ sub( /^./, CAP[substr($0,1,1)] ) ; print $0 } '
exit

From: Jean-Pierre Radley <jpr@jpradley.jpr.com>

Place this into a little editing script:

	%s;\(.\)\(.*\);\u\1\L\2
	w
	q

You'll do the deed by typing

	cat the.above.script | ex your_file

From: David Tamkin <dattier@ddsw1.mcs.com>
ex -s file << \,,
%s/\(.\)\(.*\)/\u\1\L\2/
x
,,

From: Harald.Eikrem@delab.sintef.no
#!/bin/sh

az=abcdefghijklmnopqrstuvwxyz
AZ=ABCDEFGHIJKLMNOPQRSTUVWXYZ

/bin/sed -e "
	y/$AZ/$az/
	h
	y/$az/$AZ/
	G
        s/\(.\).*\n./\1/
" $@

[ THE WINNER !!! Nice to see us Europeans can beat you Yanks :-) ]

From: Michael Gerhards <michael@jester.gun.de>

cat >script
y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/
s/^a/A/
s/^b/B/
...
repeat this for all letters
...
s/^z/Z/
<ctrl-D>

Run this script with 'sed -f script infile >outfile'
[ This was much the same as Michael J. Corrigan's script.]


From: Tim Northrup <jrsalb1!tgn@brspyr1.brs.com>
With comments by davidf@golem.waterloo.on.ca (David J. Fiander)

while read Line ; do
	First=`expr "X${Line}" : '.\(.\).*' | tr '[a-z]' '[A-Z]'`
	Rest=`expr "X${Line}" : '..\(.*\)' | tr '[A-Z]' '[a-z]'`
	echo "${First}${Rest}"
	done

-- 
Nikki Locke,Trumphurst Ltd.(PC and Unix consultancy) nikki@trmphrst.demon.co.uk
trmphrst.demon.co.uk is NOT affiliated with ANY other sites at demon.co.uk.