[clug] Next in my series of "how to do things in /bin/sh that you probably shouldn't".

steve jenkin sjenkin at canb.auug.org.au
Wed Aug 19 21:39:39 MDT 2009


Andrew Janke wrote on 20/8/09 10:39 AM:
> Suppose that I want to send all output from a shell script to a
> logfile as well as displaying it (within the script). To simply send
> everything in a shell script I can just do this somewhere near the top
> of the script:
> 
>    exec > logfile.txt 2>&1
> 
> But I am greedy, as such it is a pity you can't just do this:
> 
>    exec | tee logfile 2>&1
> 
> So instead the only way I know of doing this is as such:

<snip> Example using a named pipe.

> Seems like an awful amount of work for a simple thing, anyone know a
> better way?  Answers of "use python/perl/<some other language>" will
> be dutifully ignored.
> 
> Thanks
> 
> 
> --
> Andrew Janke
> (a.janke at gmail.com || http://a.janke.googlepages.com/)
> Canberra->Australia    +61 (402) 700 883

Andrew,

Great question. Thanks for that.

I've spent a little time playing with this - it seems like a nice thing
to do - and think I've spotted the root cause, which points to the
answer(s).

It's to do with shell creating 'pipes' and fiddling per-process file
descriptors.

When you run:

cmd1 | cmd 2

shell does this work for you:

-----
for  '|': call pipe() and save pipe_fd[0] and pipe_fd[1]

cmd1: leave fd[0] & fd[2] alone.
      close fd[1], dup pipe_fd[1] to fd[1]
      now fork & exec 'cmd1'

cmd2: leave fd[1] & fd[2] alone.
      close fd[0], dup pipe_fd[0] to fd[0]
      now fork & exec 'cmd2'

Wait for completion of both processes...
Return in "$?" the status of cmd2, ignore cmd1's ret-code.

[I could've reversed the sense of the fd[]'s from pipe()]
-----

IIRC, /bin/sh, spawns processes Right to Left, just an added wrinkle.


The essence of the problem is creating the pipe() and associated array
of fd[]'s.

There are some very smart people on this list (you know who you are) who
know much shell trickery. Perhaps they know of a way to capture a 'pipe'
and the file-descriptors at each end of it.
As in, create two processes with a pipe between them & capture the
fd[]'s, then reuse when the processes have exited.
Seems hard because of the local context of each process and passing
fd[]'s back.

I've never investigated 'csh' and descendants in detail - it may have
facilities to allow pipe & fd fiddling.

OR, doing it simply in 'standard shell' is possible only in two ways:
- create a named pipe and manipulate fd's & child processes (your soln)
- use a wrapper to get shell to automagically do the work.

AND, I could be completely wrong headed and Missed The Bleeding Obvious.
Happened before, will happen again :-)

Now, if the semantics of pipes and sockets were more similar, this might
be a little different.
Remember how Tridge solved the 'shell as IP daemon' problem by *writing*
to the socket given as stdin?
Took me by surprise & impressed the hell out me :-)

Looking forward to the continued conversation on this.

HTH
s

-- 
Steve Jenkin, Info Tech, Systems and Design Specialist.
0412 786 915 (+61 412 786 915)
PO Box 48, Kippax ACT 2615, AUSTRALIA

sjenkin at canb.auug.org.au http://members.tip.net.au/~sjenkin


More information about the linux mailing list