[comp.unix.programmer] LOST in a PIPE

bruno@skipper.dfrf.nasa.gov (Bruno Chenet) (02/26/91)

        To anybody that understands  fork(), system() or popen() 



        I am sending the command "tar tvf /dev/rst0" to list the content
 of a tape drive. I want to be able to grap each file description that shows
up on my screen as soon as possible since I want to display it in my own
X-window.
I tried to redirect the output to a file but then I cannot read this file
as long as information is redirected to it. (And this can take 5 or more
minutes when I am listing a full tape or CD). I cannot afford to let the
user waits that long.

Originally I was sending the command using a "popen()" as follow:

**********************
          sprintf(cmdline," %s ",  "tar tvf /dev/rfd0");
          if ((fp = popen(cmdline, "r")) == NULL)
                printf("popen error");

try #1:
/*
        while ((fgets(line, MAXLINE, fp)) != NULL) {
            n = strlen(line);
            if (write(1, line, n) != n)
            printf("data write error|n");
*/        


try #2: 
/*
        while ((ch = fgetc(fp)) != EOF) {
            if (ch == '\n') {
                 buf[i++] = ch;
                 fprintf(stderr," %s",buf);
                 i=0;
             }
             else   buf[i++] = ch;
        }
*/



**********************
 I should be able to read the stderr stream but I don't know how to
do it. I think that I need to use fork processing and read the stderr
until a EOL is reached. At this point I should be able to grab this line
and put it in some kind of buffer to be able to display it in my window 
and then give the control to the tar command again and so on...


I have also another problem of the same type: What happen if I send a
command that is not right. The system will answer with some kind of 
error message send to stderr. I need to grab this message and display
it in in my "report window" (I am trying to build a kind of front end
to the UNIX system using the X-Window environment so that UNIX becomes
more "user friendly"). 

Should I use "popen()", "system()", "shell scripts" or just "fork()" ?

        Please, I would take any ideas that would help me solve these
problems.


                        Thank you for your time,

                                        Bruno

jik@athena.mit.edu (Jonathan I. Kamens) (02/26/91)

  Well, you can't use system() or popen(), because neither of them allow you
to do what you want to to, i.e. read the stderr output of the tar process.  In
the former case, system(), it doesn't allow you to read any output at all.  In
the latter case, popen(), you can read stdout but not stderr, since stderr
automatically goes to whatever the stderr of your process is.

  Ah, but wait, there's a trick!  You can do this:

1. Create a pipe.

2. Fclose(stderr).

3. Fdopen() the write end of the pipe and assign the return value to stderr.

At this point, you've got a pipe connected to stderr, and you can find out
what goes into the pipe by reading from the read end of the pipe.   Then, you
can use popen() to start the tar process, and read from the FILE * returned by
the popen, or from the read end of the pipe, as appropriate.

  Since output will be going to both at the same time, you might want to use
select()  or poll() to watch both at once and read from one of them was there
is output available on it.

-- 
Jonathan Kamens			              USnail:
MIT Project Athena				11 Ashford Terrace
jik@Athena.MIT.EDU				Allston, MA  02134
Office: 617-253-8085			      Home: 617-782-0710

tchrist@convex.COM (Tom Christiansen) (02/26/91)

From the keyboard of jik@athena.mit.edu (Jonathan I. Kamens):
:
:  Well, you can't use system() or popen(), because neither of them allow you
:to do what you want to to, i.e. read the stderr output of the tar process.  In
:the former case, system(), it doesn't allow you to read any output at all.  In
:the latter case, popen(), you can read stdout but not stderr, since stderr
:automatically goes to whatever the stderr of your process is.

This isn't strictly true.  Remember that you've got the full power of
sh descriptor manipulation to help you out.  

First of all, you can dup stderr to stdout:

    if (!(fp = popen("cmd 2>&1", "r")))
	perror("couldn't popen of cmd");

Then they are read together as one merged stream.

If you want to read just stderr, not stdout, you can do this:

    fp = popen("3>&1 (cmd 2>&1 1>&3 3>&-) 3>&-", "r");

Now when you do your fgets() or whatever on that fp, you'll be reading his
stderr instead of his stdout.  His old stdout will be left unaffected,
that is, will be your program's stdout.  You can obviously redirect this
easily enough.

Now, if you really want to read both stdout and stderr separately, then
yes, you do have to more complex contortions, preferably involving
select.


--tom
-- 
"UNIX was not designed to stop you from doing stupid things, because
 that would also stop you from doing clever things." -- Doug Gwyn

 Tom Christiansen                tchrist@convex.com      convex!tchrist

mike@bria.commodore.com (02/28/91)

In an article, athena.mit.edu!jik (Jonathan I. Kamens) writes:
|  Ah, but wait, there's a trick!  You can do this:
|1. Create a pipe.
|2. Fclose(stderr).
|3. Fdopen() the write end of the pipe and assign the return value to stderr.

Why not simply dup the stderr to stdout prior to the popen()?

-- 
Michael Stefanik, MGI Inc., Los Angeles| Opinions stated are not even my own.
Title of the week: Systems Engineer    | UUCP: ...!uunet!bria!mike
-------------------------------------------------------------------------------
Remember folks: If you can't flame MS-DOS, then what _can_ you flame?