[comp.unix.programmer] Can I get back stdout after redirection?

pefv700@perv.pe.utexas.edu (05/01/91)

The subject says it all.

From what I understand, with

% cmd > file

the typical shell will do something like (omitting error checking)

    fd = fopen("file", "w");
    close(1);
    dup(fd);

Since the shell has already closed stdout's file descriptor, is it not
possible to reopen it?  (Also, how did stdout get opened in the first
place?  The shell inherited it from its parent, didn't it?)

Thanks,

Chris

jik@athena.mit.edu (Jonathan I. Kamens) (05/01/91)

In article <48186@ut-emx.uucp>, pefv700@perv.pe.utexas.edu writes:
|> Since the shell has already closed stdout's file descriptor, is it not
|> possible to reopen it?

  You can't get back stdout per se.  However, if stdout was pointing to your
terminal before the shell closed it, you can open "/dev/tty" to get a file
descriptor to write to the terminal.

|> (Also, how did stdout get opened in the first
|> place?  The shell inherited it from its parent, didn't it?)

  Yes.

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

matteo@ghost.unimi.it (Matteo Gelosa) (05/02/91)

pefv700@perv.pe.utexas.edu writes:


> The subject says it all.

> From what I understand, with

> % cmd > file

> the typical shell will do something like (omitting error checking)

>     fd = fopen("file", "w");

<     fd = open("file", O_WRONLY)_;
	probably you wanted to write this...

>     close(1);
>     dup(fd);

> Since the shell has already closed stdout's file descriptor, is it not
> possible to reopen it?  (Also, how did stdout get opened in the first
> place?  The shell inherited it from its parent, didn't it?)

	There is a special file named "/dev/tty", that always refers
	to your current tty. Simply you have to reopen it if you want
	to send your output back to your tty.
	A lot of programs that have to communicate only with a tty
	device do it to avoid redirection.
	Try with this...

< #define STDOUT 1	/* I know 1 is standard output but it looks nicer */

<	fd = open("/dev/tty", O_RDWR);	/* open /dev/tty */
<	close(STDOUT);			/* close old redirection */
<	dup(fd);			/* re-redirect standard output to tty */

	Matteo.
----
Matteo Gelosa					Phone  : +39-2-7575242
Universita' Statale di Milano			Fax    : +39-2-76110556
Dipartimento di Scienze dell'Informazione	Telex  : 335199 - MIDSII
Via Moretto da Brescia, 9                       E-Mail : matteo@ghost.unimi.it
I-20133 Milano - Italy - `92 Europe

matteo@ghost.unimi.it (Matteo Gelosa) (05/02/91)

	I was forgettin' something important...

	If you wanna work with file descriptor you have to
	do like I wrote, but if you wanna work with stdio
	buffers you can do it in a very simpler way just
	calling...

	freopen("/dev/tty", "w", stdout);

	Matteo.

meissner@osf.org (Michael Meissner) (05/04/91)

In article <1991May02.125231.20488@ghost.unimi.it>
matteo@ghost.unimi.it (Matteo Gelosa) writes:

| > Since the shell has already closed stdout's file descriptor, is it not
| > possible to reopen it?  (Also, how did stdout get opened in the first
| > place?  The shell inherited it from its parent, didn't it?)
| 
| 	There is a special file named "/dev/tty", that always refers
| 	to your current tty. Simply you have to reopen it if you want
| 	to send your output back to your tty.
| 	A lot of programs that have to communicate only with a tty
| 	device do it to avoid redirection.
| 	Try with this...

However, /dev/tty is not the same thing as stdout (though for many
users it winds up at the same location).  If you need to restore your
original stdout after redirection, the only way is not to close your
last handle on it.  I would recomend:

	orig_stdout = dup (fileno (stdout));
	if (orig_stdout < 0)
	  {
	    perror ("dup");
	    abort ();
	  }

	if (freopen ("newfile", "a", stdout) != stdout)
	  {
	    perror ("newfile");
	    abort ();
	  }

	/* whatever */

	fclose (stdout);
	if (fdopen (orig_stdout, "w") == 0)
	  {
	    perror ("fdopen");
	    abort ();
	  }

for the adventuresome, you can play games with dup behind stdio's
back, but it's not something I recomend.....

--
Michael Meissner	email: meissner@osf.org		phone: 617-621-8861
Open Software Foundation, 11 Cambridge Center, Cambridge, MA, 02142

Considering the flames and intolerance, shouldn't USENET be spelled ABUSENET?

mike@bria.UUCP (mike.stefanik) (05/04/91)

In an article, pefv700@perv.pe.utexas.edu writes:
>From what I understand, with
>
>% cmd > file
>
>the typical shell will do something like (omitting error checking)
>
>    fd = fopen("file", "w");
>    close(1);
>    dup(fd);

Acutally, I am more than a little suprised that no one has caught this
thus far ... you are mixing apples and oranges here.  The fopen()
subroutine does not hand back a descriptor that can be used with dup()
and close() -- it hands back a pointer to a "stream" which is used
with fprintf(), fclose(), etc.

What you mean is:

	fd = open("file",O_WRONLY|O_CREAT|O_TRUNC,0666);
	close(1);
	dup(fd);

>Since the shell has already closed stdout's file descriptor, is it not
>possible to reopen it?  (Also, how did stdout get opened in the first
>place?  The shell inherited it from its parent, didn't it?)

Assuming that we are talking about a login shell, the shell inherits
stdin, stdout and stderr (descriptors 0, 1 and 2 respectively) from
the process that invoked it ... namely /bin/login ( /bin/login likewise
inherited these descriptors from /etc/getty )

Acutally, the above algorithm that you provide has a problem, namely
that once you close the descriptor (in this case, stdout) you have no
idea how to reopen it after the redirector has completed.  Several
people have mentioned /dev/tty ... but this is NOT a solution.  What
happens when this instance of the shell was not writing to /dev/tty, but
a regular file instead?

The solution is to use dup() to duplicate the stdout descriptor, and
then use dup2() to force duplication of the file descriptor over stdout.
When the redirection is complete, you need only dup2() the original
descriptor that you preserved.

IMHO, it is an ugly practice to take "advantage" of the fact that dup()
will always return the lowest file descriptor.  Here is how I do it:

#include <stdio.h>
#include <fcntl.h>

#define STDIN	0	/* the standard input descriptor */
#define STDOUT	1	/* the standard output descriptor */
#define STDERR	2	/* the standard error descriptor */

main()
{
int oldout, fd;

	puts("writing output of /bin/ls to outputfile");

	/*
	 *  first we create the file that we are going to redirect
	 *  output to;  some stuff folks might claim that creat()
	 *  is archaic, but I like it nonetheless
	*/

	if ( (fd = creat("outputfile",0666)) == -1 ) {
		perror("open");
		return 1;
		}

	/*
	 *  now we make a duplicate of the *current* standard output
	 *  descriptor; this is how we can put things back correctly
	 *  after the redirection
	*/

	oldout = dup(STDOUT);

	/*
	 *  now we force the duplication of the file descriptor
	 *  over the standard output descriptor; the standard output
	 *  descriptor is closed, but since we have `oldout' which
	 *  points to the original standard output, we don't care
	*/

	if ( dup2(fd,STDOUT) == -1 ) {
		perror("dup2");
		return 1;
		}

	/*
	 *  close the file descriptor because we don't need it any more
	 *  (standard output is now that file)
	*/

	close(fd);

	/*
	 *  execute some command that generates output so that we
	 *  know that this really works
	*/

	if ( fork() )
		wait(NULL);
	else
		execlp("/bin/ls","ls","-l",NULL);

	/*
	 *  since we're done, we need to put the original descriptor
	 *  for standard output back in place;  it is forced over
	 *  the current stdout descriptor (which will close the file)
	 *  and stdout is back to whatever it was before
	*/

	if ( dup2(oldout,STDOUT) == -1 ) {
		perror("dup2");
		return 1;
		}

	/*
	 *  the `oldout' is no longer needed, so we close it
	*/

	close(oldout);

	/*
	 *  just to make sure that things are back the way that
	 *  they should be :-)
	*/

	puts("the output of /bin/ls has been successfully redirected");

	return 0;
}

Just some kibbles for thought.
-- 
Michael Stefanik, MGI Inc, Los Angeles | Opinions stated are never realistic
Title of the week: Systems Engineer    | UUCP: ...!uunet!bria!mike
-------------------------------------------------------------------------------
If MS-DOS didn't exist, who would UNIX programmers have to make fun of?