[comp.unix.wizards] Unlinked temp files in sh scripts

dce@Solbourne.COM (David Elliott) (04/25/89)

This is a trick that Dave Hitz (hitz@auspex.com) and I worked
out last year:

	exec 3>temp.$$ 4<temp.$$
	rm -f temp.$$

At this point, you can put all the data you want in the temp
file by redirecting to fd 3, as in

	generate_data 1>&3

After this has been done, fd 4 is still a read descriptor
pointing at the beginning of the temp file, so you can
read the data in the file by redirecting from fd 4, or,
if you need to read it multiple time, by dup'ing fd 4
("exec 5<&4", for example, to copy it to fd 5).

When you're finished, there's no need to clean up, since
the file was removed before it was even used.  Of course,
it is possible to kill the program between the time the
temp file is created and the time when it is removed, but
at worst you'll end up with an empty file.

-- 
David Elliott		dce@Solbourne.COM
			...!{boulder,nbires,sun}!stan!dce

dce@Solbourne.COM (David Elliott) (04/26/89)

In article <871@marvin.Solbourne.COM> dce@Solbourne.com (David Elliott) writes:
>
>After this has been done, fd 4 is still a read descriptor
>pointing at the beginning of the temp file, so you can
>read the data in the file by redirecting from fd 4, or,
>if you need to read it multiple time, by dup'ing fd 4
>("exec 5<&4", for example, to copy it to fd 5).

Oops.  Looks like I screwed up here.  You can't do an "exec 5<&4"
because 5 and 4 will still point to the same place.  Instead, you have
to do multiple redirections before the temp file is removed.

This is kind of sad, but I don't think you generally need to make more
than a couple of passes on the temp file.

-- 
David Elliott		dce@Solbourne.COM
			...!{boulder,nbires,sun}!stan!dce

leo@philmds.UUCP (Leo de Wit) (04/27/89)

In article <871@marvin.Solbourne.COM> dce@Solbourne.com (David Elliott) writes:
|
|This is a trick that Dave Hitz (hitz@auspex.com) and I worked
|out last year:
|
|	exec 3>temp.$$ 4<temp.$$
|	rm -f temp.$$

For C programs this is already a common technique when dealing with
temp files:  open a file (giving you a descriptor), then unlink it. As
long as the file is not closed, it still can be accessed via the
descriptor. Perhaps it is not so commonly used in sh scripts.

|At this point, you can put all the data you want in the temp
|file by redirecting to fd 3, as in
|
|	generate_data 1>&3
|
|After this has been done, fd 4 is still a read descriptor
|pointing at the beginning of the temp file, so you can
|read the data in the file by redirecting from fd 4, or,
|if you need to read it multiple time, by dup'ing fd 4
|("exec 5<&4", for example, to copy it to fd 5).

This doesn't work, since 5<&4 effectively is dup2(4,5). And although
this gives you a new descriptor, it will have only one seek pointer in
the file.  This means if you read to EOF on 4, you're also on EOF on
5. Example:

----------- Start of sample program -----------
#! /bin/sh
exec 3>temp.$$ 4<temp.$$
exec 5<&4
rm -f temp.$$

cat >&3 <<EOT
first
second
third
EOT

echo From 4:
cat <&4

# rewind 5

echo From 5:
cat <&5
----------- End of sample program -------------
----------- Output of sample program ----------
From 4:
first
second
third
From 5:
----------- End of output of sample program ---

The shell has no means to rewind that I know of; however, a simple
one-liner will do the trick (add error checking of your fancy):

----- rewind.c:
main(argc,argv) int argc; char **argv; { lseek(atoi(argv[1]),0L,0); }

After uncommenting the '# rewind 5' the output from 5 is the same as
that from 4!

	 Leo.

P.S. The descriptor '5' is not needed after all; all one has to do is
rewind 4 instead of rewind 5, and then reuse descriptor 4.

dce@Solbourne.COM (David Elliott) (04/29/89)

In article <1015@philmds.UUCP> leo@philmds.UUCP (Leo de Wit) writes:
>In article <871@marvin.Solbourne.COM> dce@Solbourne.com (David Elliott) writes:
>|	exec 3>temp.$$ 4<temp.$$
>|	rm -f temp.$$
>For C programs this is already a common technique when dealing with
>temp files:  open a file (giving you a descriptor), then unlink it. As
>long as the file is not closed, it still can be accessed via the
>descriptor. Perhaps it is not so commonly used in sh scripts.

Give this man a cigar!

Seriously, I guess I should have pointed out that the trick here was to
duplicate the well-known C technique in shell.

In my 6 years as a shell programmer, I had never seen this technique
applied to the shell (which is why I felt that it was useful to post
the idea).  In general, shell programmers either trap 0 (exit from
shell), trap a whole slew of signals, or just assume that the shell
script will exit cleanly.

Most people assume that programming in shell is a bad idea because
of all the things you can't do.  I enjoy finding ways to do things
that appear to be impossible to do in sh, if only to say "nyaah"
to people who make statements like "you shouldn't program in sh
because it isn't a structured programming language" (not that this
trick disproves that, but...)

-- 
David Elliott		dce@Solbourne.COM
			...!{boulder,nbires,sun}!stan!dce

gwc@root.co.uk (Geoff Clare) (05/02/89)

In article <871@marvin.Solbourne.COM> dce@Solbourne.com (David Elliott) writes:
>
>This is a trick that Dave Hitz (hitz@auspex.com) and I worked
>out last year:
>
>	exec 3>temp.$$ 4<temp.$$
>	rm -f temp.$$

A neat idea, but unfortunately it suffers from one big problem - those
hard-coded file descriptor numbers.

Suppose I execute a shell script which uses this trick (although I am
unaware of it) and I use file descriptor 3 on the command line for
some reason.  The result could be disastrous.

The idea could be made to work by having the script execute a program
which returns two free file descriptor numbers.  But is it worth the
bother, just to avoid a "trap" statement?

A neater solution could be had if there was a predefined shell
variable (like $$, $#, etc.) giving the next available file
descriptor.  Maybe the Korn shell has such a thing?
-- 

Geoff Clare    UniSoft Limited, Saunderson House, Hayne Street, London EC1A 9HH
gwc@root.co.uk   ...!mcvax!ukc!root44!gwc   +44-1-315-6600  FAX: +44-1-315-6622

flint@gistdev.UUCP (05/06/89)

If you are going to do this with the Korn shell, I think you're better off
using a co-process, something like this:

cat |&
print -p "This stuff gets written to the co-process."
read -p readup
#  Now ${readup} contains the stuff read back from the co-process.

You can also redirect the input and/or output pipes of the co-process to
a numbered file descriptor if you want, without having to have a temporary
file sitting in the middle to clean up after.  The other advantage is that
the co-process can be a whole pipeline to filter the stuff you are running
through it, as opposed to simply spitting back what it was given (as the
cat above would do: any pipeline works in place of the cat.)