@@ -13,6 +13,7 @@ var child = require('child_process');
1313function execSync ( cmd , opts ) {
1414 var tempDir = _tempDir ( ) ;
1515 var stdoutFile = path . resolve ( tempDir + '/' + common . randomFileName ( ) ) ,
16+ stderrFile = path . resolve ( tempDir + '/' + common . randomFileName ( ) ) ,
1617 codeFile = path . resolve ( tempDir + '/' + common . randomFileName ( ) ) ,
1718 scriptFile = path . resolve ( tempDir + '/' + common . randomFileName ( ) ) ,
1819 sleepFile = path . resolve ( tempDir + '/' + common . randomFileName ( ) ) ;
@@ -21,19 +22,30 @@ function execSync(cmd, opts) {
2122 silent : common . config . silent
2223 } , opts ) ;
2324
24- var previousStdoutContent = '' ;
25- // Echoes stdout changes from running process, if not silent
26- function updateStdout ( ) {
27- if ( options . silent || ! fs . existsSync ( stdoutFile ) )
25+ var previousStdoutContent = '' ,
26+ previousStderrContent = '' ;
27+ // Echoes stdout and stderr changes from running process, if not silent
28+ function updateStream ( streamFile ) {
29+ if ( options . silent || ! fs . existsSync ( streamFile ) )
2830 return ;
2931
30- var stdoutContent = fs . readFileSync ( stdoutFile , 'utf8' ) ;
32+ var previousStreamContent ,
33+ proc_stream ;
34+ if ( streamFile === stdoutFile ) {
35+ previousStreamContent = previousStdoutContent ;
36+ proc_stream = process . stdout ;
37+ } else { // assume stderr
38+ previousStreamContent = previousStderrContent ;
39+ proc_stream = process . stderr ;
40+ }
41+
42+ var streamContent = fs . readFileSync ( streamFile , 'utf8' ) ;
3143 // No changes since last time?
32- if ( stdoutContent . length <= previousStdoutContent . length )
44+ if ( streamContent . length <= previousStreamContent . length )
3345 return ;
3446
35- process . stdout . write ( stdoutContent . substr ( previousStdoutContent . length ) ) ;
36- previousStdoutContent = stdoutContent ;
47+ proc_stream . write ( streamContent . substr ( previousStreamContent . length ) ) ;
48+ previousStreamContent = streamContent ;
3749 }
3850
3951 function escape ( str ) {
@@ -42,6 +54,7 @@ function execSync(cmd, opts) {
4254
4355 if ( fs . existsSync ( scriptFile ) ) common . unlinkSync ( scriptFile ) ;
4456 if ( fs . existsSync ( stdoutFile ) ) common . unlinkSync ( stdoutFile ) ;
57+ if ( fs . existsSync ( stderrFile ) ) common . unlinkSync ( stderrFile ) ;
4558 if ( fs . existsSync ( codeFile ) ) common . unlinkSync ( codeFile ) ;
4659
4760 var execCommand = '"' + process . execPath + '" ' + scriptFile ;
@@ -59,14 +72,16 @@ function execSync(cmd, opts) {
5972 " fs.writeFileSync('" + escape ( codeFile ) + "', err ? err.code.toString() : '0');" ,
6073 "});" ,
6174 "var stdoutStream = fs.createWriteStream('" + escape ( stdoutFile ) + "');" ,
75+ "var stderrStream = fs.createWriteStream('" + escape ( stderrFile ) + "');" ,
6276 "childProcess.stdout.pipe(stdoutStream, {end: false});" ,
63- "childProcess.stderr.pipe(stdoutStream , {end: false});" ,
77+ "childProcess.stderr.pipe(stderrStream , {end: false});" ,
6478 "childProcess.stdout.pipe(process.stdout);" ,
6579 "childProcess.stderr.pipe(process.stderr);" ,
6680 "var stdoutEnded = false, stderrEnded = false;" ,
67- "function tryClosing(){ if(stdoutEnded && stderrEnded){ stdoutStream.end(); } }" ,
68- "childProcess.stdout.on('end', function(){ stdoutEnded = true; tryClosing(); });" ,
69- "childProcess.stderr.on('end', function(){ stderrEnded = true; tryClosing(); });"
81+ "function tryClosingStdout(){ if(stdoutEnded){ stdoutStream.end(); } }" ,
82+ "function tryClosingStderr(){ if(stderrEnded){ stderrStream.end(); } }" ,
83+ "childProcess.stdout.on('end', function(){ stdoutEnded = true; tryClosingStdout(); });" ,
84+ "childProcess.stderr.on('end', function(){ stderrEnded = true; tryClosingStderr(); });"
7085 ] . join ( '\n' ) ;
7186
7287 fs . writeFileSync ( scriptFile , script ) ;
@@ -80,7 +95,7 @@ function execSync(cmd, opts) {
8095 // Welcome to the future
8196 child . execSync ( execCommand , execOptions ) ;
8297 } else {
83- cmd += ' > ' + stdoutFile + ' 2>&1' ; // works on both win/unix
98+ cmd += ' > ' + stdoutFile + ' 2> ' + stderrFile ; // works on both win/unix
8499
85100 var script = [
86101 "var child = require('child_process')" ,
@@ -98,8 +113,9 @@ function execSync(cmd, opts) {
98113 // sleepFile is used as a dummy I/O op to mitigate unnecessary CPU usage
99114 // (tried many I/O sync ops, writeFileSync() seems to be only one that is effective in reducing
100115 // CPU usage, though apparently not so much on Windows)
101- while ( ! fs . existsSync ( codeFile ) ) { updateStdout ( ) ; fs . writeFileSync ( sleepFile , 'a' ) ; }
102- while ( ! fs . existsSync ( stdoutFile ) ) { updateStdout ( ) ; fs . writeFileSync ( sleepFile , 'a' ) ; }
116+ while ( ! fs . existsSync ( codeFile ) ) { updateStream ( stdoutFile ) ; fs . writeFileSync ( sleepFile , 'a' ) ; }
117+ while ( ! fs . existsSync ( stdoutFile ) ) { updateStream ( stdoutFile ) ; fs . writeFileSync ( sleepFile , 'a' ) ; }
118+ while ( ! fs . existsSync ( stderrFile ) ) { updateStream ( stderrFile ) ; fs . writeFileSync ( sleepFile , 'a' ) ; }
103119 }
104120
105121 // At this point codeFile exists, but it's not necessarily flushed yet.
@@ -110,10 +126,12 @@ function execSync(cmd, opts) {
110126 }
111127
112128 var stdout = fs . readFileSync ( stdoutFile , 'utf8' ) ;
129+ var stderr = fs . readFileSync ( stderrFile , 'utf8' ) ;
113130
114131 // No biggie if we can't erase the files now -- they're in a temp dir anyway
115132 try { common . unlinkSync ( scriptFile ) ; } catch ( e ) { }
116133 try { common . unlinkSync ( stdoutFile ) ; } catch ( e ) { }
134+ try { common . unlinkSync ( stderrFile ) ; } catch ( e ) { }
117135 try { common . unlinkSync ( codeFile ) ; } catch ( e ) { }
118136 try { common . unlinkSync ( sleepFile ) ; } catch ( e ) { }
119137
@@ -124,22 +142,24 @@ function execSync(cmd, opts) {
124142 // True if successful, false if not
125143 var obj = {
126144 code : code ,
127- output : stdout
145+ output : stdout ,
146+ stderr : stderr
128147 } ;
129148 return obj ;
130149} // execSync()
131150
132151// Wrapper around exec() to enable echoing output to console in real time
133152function execAsync ( cmd , opts , callback ) {
134153 var output = '' ;
154+ var stderr = '' ;
135155
136156 var options = common . extend ( {
137157 silent : common . config . silent
138158 } , opts ) ;
139159
140160 var c = child . exec ( cmd , { env : process . env , maxBuffer : 20 * 1024 * 1024 } , function ( err ) {
141161 if ( callback )
142- callback ( err ? err . code : 0 , output ) ;
162+ callback ( err ? err . code : 0 , output , stderr ) ;
143163 } ) ;
144164
145165 c . stdout . on ( 'data' , function ( data ) {
@@ -149,9 +169,9 @@ function execAsync(cmd, opts, callback) {
149169 } ) ;
150170
151171 c . stderr . on ( 'data' , function ( data ) {
152- output += data ;
172+ stderr += data ;
153173 if ( ! options . silent )
154- process . stdout . write ( data ) ;
174+ process . stderr . write ( data ) ;
155175 } ) ;
156176
157177 return c ;
@@ -174,16 +194,17 @@ function execAsync(cmd, opts, callback) {
174194//@ /* ... do something with data ... */
175195//@ });
176196//@
177- //@ exec('some_long_running_process', function(code, output) {
197+ //@ exec('some_long_running_process', function(code, output, stderr ) {
178198//@ console.log('Exit code:', code);
179199//@ console.log('Program output:', output);
200+ //@ console.log('Program stderr:', stderr);
180201//@ });
181202//@ ```
182203//@
183- //@ Executes the given `command` _synchronously_, unless otherwise specified.
184- //@ When in synchronous mode returns the object `{ code:..., output:... }`, containing the program's
185- //@ `output` (stdout + stderr) and its exit `code`. Otherwise returns the child process object, and
186- //@ the `callback` gets the arguments `(code, output)`.
204+ //@ Executes the given `command` _synchronously_, unless otherwise specified. When in synchronous
205+ //@ mode returns the object `{ code:..., output:... , stderr :... }`, containing the program's
206+ //@ `output` (stdout), ` stderr`, and its exit `code`. Otherwise returns the child process object,
207+ //@ and the `callback` gets the arguments `(code, output, stderr )`.
187208//@
188209//@ **Note:** For long-lived processes, it's best to run `exec()` asynchronously as
189210//@ the current synchronous implementation uses a lot of CPU. This should be getting
0 commit comments