Menu

#9 Patch to fix redirecting stderr in exec on Windows

obsolete: 8.0p2
closed-duplicate
nobody
1
2001-04-05
2000-10-26
Anonymous
No

OriginalBugID: 582 Bug
Version: 8.0p2
SubmitDate: '1998-05-21'
LastModified: '2000-04-26'
Severity: MED
Status: Closed
Submitter: hershey
ChangedBy: hobbs
RelatedBugIDs: 682
OS: All Windows
FixedDate: '2000-10-25'
FixedInVersion: NA
ClosedDate: '2000-04-26'

Consider the following Tcl script:

set version [exec gcc -v 2>@ stdout]

if {$version != ""} then {

puts "Got version"

} else {

puts "***FAILED***"

}

The command `gcc -v' normally outputs version information on stderr.

The 2>@ stdout is intended to redirect the stderr output to stdout, so

that it can be easily read using the exec command.

This commands works as expected on Unix. However, when using Tcl

8.0p2, it fails on Windows.

What happens is that when TclCreatePipeline sees the 2>@ stdout, it

calls FileForRedirect, which finds the stdout channel and then calls

TclpMakeFile. On Unix, this returns a code indicating descriptor 1.

On Windows, this returns a file handle for stdout.

Since the exec command does not redirect stdout, TclCreatePipeline

winds up calling TclCreatePipe to create a pipe for stdout.

TclCreatePipeline then calls TclpCreateProcess.

On Unix, this forks, and the child first calls SetupStdFile for

stdout, and then calls it for stderr. Calling SetupStdFile for stdout

dups the stdout pipe onto file descriptor 1. Calling SetupStdFile for

stderr file descriptor 1, as returned by TclpMakeFile when called by

FileForRedirect, onto file descriptor 2. So everything works as

expected: both stdout and stderr are redirected to the same file.

On Windows, however, stdout and stderr are handled differently. In

this case, stdout will be redirected onto the stdout pipe as expected.

However, when stderr is redirected, it will be redirected from the old

stdout handle, rather than the new one. The effect is that stdout

will go to the pipe, and stderr will go to the console.

In other words, using 2>@ stderr happens to work on Unix because of

the way in which the redirection descriptor is represented, and the

order in which the descriptors are redirected. On Windows, which does

not always use the same handle number for stdout, it fails.

I have appended a patch which fixes this problem on Windows, by

changing the way in which TclCreatePipeline handles a redirection of

stderr on stdout. I changed TclCreatePipeline to check for that case

specially. When it happens, we arrange to call TclpCreateProcess with

the same handle for both stdout and stderr. This code works correctly

on both Unix and Windows. On Unix it is, perhaps, less fragile.

We are using this as a local patch at Cygnus. I would like some

version of this patch to be included in future Tcl releases.

Actually, to be more precise, I would like the above Tcl script to

work on Windows in future releases, and this patch is one way to make

that happen. Please let me know if I can help further.

Ian Taylor

ian@cygnus.com

*** /5g/ian/tcl8.0p2/generic/tclPipe.c Wed Jun 25 13:39:57 1997

--- tclPipe.c Fri Apr 10 14:37:15 1998

*************** TclCreatePipeline(interp, argc, argv, pi

*** 497,502 ****

--- 497,504 ----

int errorClose = 0; /* If non-zero, then errorFile should be

* closed when cleaning up. */

int errorRelease = 0;

+ int joinError = 0; /* If non-zero, stderr is being redirected

+ * onto stdout. */

char *p;

int skip, lastBar, lastArg, i, j, atOK, flags, errorToOutput;

Tcl_DString execBuffer;

*************** TclCreatePipeline(interp, argc, argv, pi

*** 665,674 ****

errorRelease = 0;

TclpReleaseFile(errorFile);

}

! errorFile = FileForRedirect(interp, p, atOK, argv[i],

! argv[i + 1], flags, &skip, &errorClose, &errorRelease);

! if (errorFile == NULL) {

! goto error;

}

break;

}

--- 667,691 ----

errorRelease = 0;

TclpReleaseFile(errorFile);

}

!

! /* Check specially for the case of redirecting stderr onto

! stdout. */

! joinError = 0;

! if (*p == '@'

! && strcmp ((p[1] == '\0' ? argv[i + 1] : p + 1),

! "stdout") == 0) {

! joinError = 1;

! if (p[1] == '\0') {

! skip = 2;

! } else {

! skip = 1;

! }

! } else {

! errorFile = FileForRedirect(interp, p, atOK, argv[i],

! argv[i + 1], flags, &skip, &errorClose, &errorRelease);

! if (errorFile == NULL) {

! goto error;

! }

}

break;

}

*************** TclCreatePipeline(interp, argc, argv, pi

*** 755,761 ****

}

if (errorFile == NULL) {

! if (errFilePtr != NULL) {

/*

* Set up the standard error output sink for the pipeline, if

* requested. Use a temporary file which is opened, then deleted.

--- 772,780 ----

}

if (errorFile == NULL) {

! if (joinError) {

! errorFile = outputFile;

! } else if (errFilePtr != NULL) {

/*

* Set up the standard error output sink for the pipeline, if

* requested. Use a temporary file which is opened, then deleted.

Discussion

  • Brent B. Welch

    Brent B. Welch - 2000-10-26
    • priority: 5 --> 1
    • status: open --> closed-duplicate
     
  • Don Porter

    Don Porter - 2001-04-05
    • labels: 104241 --> 27. Channel Types