使用Node.js执行含Expect命令的脚本时无法捕获标准输出?
Oh man, I’ve run into this exact issue before—super frustrating when your Expect script works perfectly in the terminal but dies when you call it from Node.js! The root problem almost always boils down to Expect needing an interactive pseudo-terminal (pty) to capture output properly, and Node’s exec method doesn’t provide one by default. Let’s break down why this happens and how to fix it.
Why Does This Happen?
When you run your script directly in the terminal, it’s attached to a real terminal session. Expect relies on this environment to listen for and interact with the output of the commands it’s automating. But Node’s exec uses pipes to capture stdout and stderr instead of a pty. Many commands (especially those designed for interactive use) behave differently in pipe mode—they might buffer output differently, skip terminal-specific formatting, or even refuse to produce the exact output your Expect script is waiting for. That’s why your matches fail.
Solutions to Try
1. Use node-pty to Provide a Pseudo-Terminal
The most reliable fix is to use a library that creates a proper pty for your script to run in. node-pty is the go-to for this. Here’s how to set it up:
First, install the library:
npm install node-pty
Then modify your Node.js code to spawn the script in a pty:
const pty = require('node-pty'); // Spawn your script in a pseudo-terminal const scriptProcess = pty.spawn('./your-expect-script.sh', [], { name: 'xterm-color', cols: 80, rows: 30, cwd: process.cwd(), env: process.env }); // Listen for output from the script scriptProcess.on('data', (output) => { console.log('Script output:', output); }); // Handle script exit scriptProcess.on('exit', (exitCode) => { console.log(`Script exited with code: ${exitCode}`); });
This gives your Expect script the interactive terminal environment it needs to capture output correctly.
2. Wrap Your Script in script (No Extra Libraries)
If you don’t want to add a dependency, you can use the Unix script command to simulate a terminal session. This trick fools Expect into thinking it’s running in a real terminal.
Modify your Node.js exec call to wrap the script:
const { exec } = require('child_process'); // Use script to simulate a terminal exec('script -q -c "./your-expect-script.sh" /dev/null', (err, stdout, stderr) => { if (err) { console.error('Error:', err); return; } console.log('Script output:', stdout); });
The -q flag suppresses script’s own startup messages, and /dev/null tells it not to log to a file. This should make your Expect script behave just like it does in the terminal.
3. Tweak Your Expect Script for Pipe Compatibility
Sometimes the issue is with how your Expect script is configured. Try these adjustments:
- Make sure you’re not relying on terminal-specific output (like ANSI color codes) that get stripped in pipe mode. Update your expect patterns to match the non-terminal output.
- Explicitly disable output buffering in the commands your Expect script runs. For example, if you’re running a command that buffers output by default, add flags like
unbuffer(from theexpectpackage) to force immediate output. - Add
log_user 1at the top of your Expect script to force it to log output, which can help debug if it’s actually capturing anything.
Quick Debug Tip
To verify if the output differs between terminal and pipe, run your script directly in the terminal and save the output to a file:
./your-expect-script.sh > terminal-output.txt
Then run it via Node’s exec and save that output:
exec('./your-expect-script.sh', (err, stdout) => { require('fs').writeFileSync('pipe-output.txt', stdout); });
Compare the two files—you’ll likely see differences that explain why your Expect patterns aren’t matching.
内容的提问来源于stack exchange,提问作者Rasoul Salehi

