dan@rna.UUCP (Dan Ts'o) (05/09/84)
Hi, I've just started to use the Bourne shell in non-trivial command script writing and have run into a number of problems. I am converting some shell scripts from an older shell (V6-like with variables). In particular, 1) How do you read a single line from /dev/tty (or an arbitrary file, NOT stdin) and assign that line to a variable ? 2) How do you arrange for a single instance of common shell code ? The Bourne shell has no procedures and no goto statement. For reading a single line into a variable from /dev/tty, I've had to use the ridiculous: a=`dd if=/dev/tty count=1 2>/dev/null` or a=`(read a echo $a) < /dev/tty` The read command doesn't seem to like its stdin re-directed (I know its a builtin command). There appears to a possibility of making /dev/tty the stdin by saying exec 0< /dev/tty but this throw away the old stdin. Even if these two problems have easy solution which I've overlooked, I'd like to say that the Bourne shell sucks. If people are accurate in saying the C-shell is worst than the Bourne shell for programming... I know the Korn shell has shell procedures and all its builtin commands are supposed to be I/O redirectable. Cheers, Dan Ts'o ...cmcl2!rna!dan
matt@UCLA-LOCUS.ARPA (05/13/84)
From: "Matthew J. Weinstein" <matt@UCLA-LOCUS.ARPA> >Date: 8 May 84 16:06:31-PDT (Tue) >To: info-unix@BRL-VGR.ARPA >From: hplabs!hao!seismo!cmcl2!rna!dan@ucb-vax.ARPA >Subject: Bourne shell programming question... > >Article-I.D.: rna.250 > >Hi, > I've just started to use the Bourne shell in non-trivial >command script writing and have run into a number of problems. I am >converting some shell scripts from an older shell (V6-like with variables). > In particular, > 1) How do you read a single line from /dev/tty (or an arbitrary file, > NOT stdin) and assign that line to a variable ? 1) Sh doesn't seem to handle redirection properly for read. So, use a program that reads one line from stdin and exits. This could be the `head' program in 4.1: READ="head -1" ... a=`$READ </dev/tty` The short C program: #include <stdio.h> main() { int c; while ((c=getchar()) != EOF) { putchar(c); if (c=='\n') break; } } also approximates a solution. > 2) How do you arrange for a single instance of common shell code ? > The Bourne shell has no procedures and no goto statement. > 2) Procedures are possible, but each one has to be a shell script (to the best of my knowledge). Tricky scripts combined with eval can usually get some interesting results (nothing quite replaces local vars). Try something like: var='commands' ... eval "$var" You can (sort of) pass params to this with the subterfuge: param1="blah.." param2="foo.." eval "$var" A short example: proc='for i in $arg; do echo arg:$i ; done' arg="alpha beta gamma" eval $proc arg="A B C" eval $proc Note that `;'s etc. have to be in the right place here, since the multiples lines are scrunched together. There may also be quoting losses. This is all a bit ad-hoc (and it's late at night too). Anyone have better suggestions (please!)? - Matt
merlyn@sequent.UUCP (05/14/84)
> From: dan@rna.UUCP > Message-ID: <250@rna.UUCP> > Date: Tue, 8-May-84 16:06:31 PDT > > Hi, > I've just started to use the Bourne shell in non-trivial > command script writing and have run into a number of problems. I am > converting some shell scripts from an older shell (V6-like with variables). > In particular, > 1) How do you read a single line from /dev/tty (or an arbitrary file, > NOT stdin) and assign that line to a variable ? > 2) How do you arrange for a single instance of common shell code ? > The Bourne shell has no procedures and no goto statement. First, number 2. Can't, at least not without stashing it in a separate file and calling it as a subroutine (very icky). Also, they call up (at least) one more process. Time consuming on most Un*xes. Second, number 1. I agree that your solutions leave lots to be desired. Bourne himself in his (not real great) book "The Unix System", gives this flavor of a solution: exec 3<&0 </dev/tty read foo exec <&3 3<&- if you can believe that. For a bit of explanation, this maximal esotericism does the following: (1) the first line copies stdin (whatever that is) to file descriptor "3" (the first one after stdin=0, stdout=1, and stderr=2), and at the same time, reopens stdin to be /dev/tty. This is all done without starting a new process... exec is one of the few builtins that allows I/O redirection. (2) the second line does the read into shell variable "foo" from /dev/tty. (3) the third line copies file descriptor 3 back to stdin, and then closes file descriptor 3. I kinda like this one, since it doesn't use any new processes. But, only the author of the Bourne shell (and a very esoteric one at that) would resort to coming up with such bizarre uses for that little-used feature of assigning specific file descriptors to specific files. [Slight editorial comment.] Another favorite, although it still needs a separate process is foo="`head -1 /dev/tty`" but you have to be using a Berkeley Un*x to take advantage of that. (You could write a quicky C program to do the same thing.) -- A particularly personal and original observation from the thought-stream of Randal L. ("</dev/null >&0 2>&1") Schwartz, esq. (merlyn@sequent.UUCP) (Official Legendary Sorcerer of the 1984 Summer Olympics) Sequent Computer Systems, Inc. (503)626-5700 (sequent = 1/quosine) UUCP: {decwrl,ogcvax,pur-ee,rocks34,shell,unisoft,vax135,verdix}!sequent!merlyn P.S. Unix is a trademark of some divested part of TPC. (Who owns that now?) P.P.S. I have no personal gripes with Bourne. I just happen to notice that some of the things that he does are a bit odd. "Pay no attention to the man behind the curtain."
ka@hou3c.UUCP (Kenneth Almquist) (05/14/84)
If you have the line(1) program, you can read a line from the terminal by saying: a=`line < /dev/tty` As you point out, it is also possible to use exec to redirect the standard input (allowing you to use read). You can move stdin to a different file descriptor first if you will need it later: exec 3<&0 </dev/tty # open /dev/tty as stdin read a # read a line from the terminal exec <&3 3<&- # restore original standard input Kenneth Almquist
avr@CS-Arthur (Andrew V Royappa) (05/15/84)
x <-- 'x' to read a line off a file: ----- a=`head -1 $file` ----- If $file is null, stdin will be read. For shared code, you could do ----- CODE="lines of code separated by ; " eval $CODE # use code ----- If you do complicated things with quotes, you'll probably have to backslash a lot (backslash ? backstab ?). Another way to do this is to put all commands in a file (in /tmp/code.$$ or such), do eval `cat <file>`, and remove the file later. Andrew Royappa {ucbvax,decvax,ihnp4,pur-ee}!purdue!avr yep, I should use 'r', but something weird happened when I did.