Advanced shell scripting question :-)

Derek D. Martin ddm+gnhlug at pizzashack.org
Wed Sep 4 19:55:35 EDT 2002


-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

At some point hitherto, Steven W. Orr hath spake thusly:
> 1. I want both stdout and stderr to go to the screen
> 
> 2. I want stdout and stderr combined in a file
> 
> 3. I want an error log file to only contain stderr.
[snip]
> Any takers?

Using only standard shell tools, this is not possible, as far as I can
see.  It might be possible in ksh, but I'm not overly familiar with
all the neat tricks you can do with file descriptors in ksh...

However, it would be fairly easy to write a C program to do this.
You'd have to:

  0. open the output file and the error file.  These can probably best
     be passed to the program as command-line args.
  1. set up a pair of pipes, one for stdout and the other for stderr
  2. This gives you four file descriptors: pipe1in, pipe1out, pipe2in,
     and pipe2out.  However, per the semantics of the pipe system
     call, these would be respectively: pipe1[0], pipe1[1], pipe2[0],
     and pipe2[1].  

  3. fork()
  4. in the child, use the dup2 syscall:

     dup2(pipe1[1],1);
     dup2(pipe2[1],2);

     This "redirects" stdout and stderr (respectively) to pipe1 and
     pipe2, which the parent can read from pipe1[0] and pipe2[0].

  5. in the child process, execv() your program.

  6. in the parent (from here on in), use some sort of I/O routines on
     pipe1[0] and pipe2[0] to read all of your program's output (I'm
     thinking probably select(), here).

  7. Take all the input from pipe1[0] and print it seperately to 
        a) stdout
        b) your output file

  8. Take all the input from pipe2[0] and print it seperately to
        a) stderr
        b) your output file
        c) your error file

  9. lather, rinse, and repeat, until there's no more output.


This can probably also be done with perl, but the procedure is
essentially the same, and the code will be very similar.  Unless
one of the perl wizards on the list know a trick for this...

BTW, for the sake of simplicity, I ignored handling command line args.
If your program does not require arguments, or they are always the
same, it's probably easiest to hard-code them in the call to execv().
Otherwise, for simplicity, just code the program so that argv[1] is
the output file, argv[2] is the error file, argv[3] is the program
name, and argv[>3] are the arguments to your program.

Also note that all references to execv() should be replaced with your
favorite variant of the system call.  Probably execvp(), so you can
use the PATH to find the program...  :)

HTH.

- -- 
Derek Martin               ddm at pizzashack.org    
- ---------------------------------------------
I prefer mail encrypted with PGP/GPG!
GnuPG Key ID: 0x81CFE75D
Retrieve my public key at http://pgp.mit.edu
Learn more about it at http://www.gnupg.org
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.0.6 (GNU/Linux)
Comment: For info see http://www.gnupg.org

iD8DBQE9dp12djdlQoHP510RAvppAKCugnfO8AhPYDT3F27/1+Xzf4+0gACfQm5i
38riGnZupPap9dhyvX4wMWA=
=Seb6
-----END PGP SIGNATURE-----



More information about the gnhlug-discuss mailing list