Exec: แสดง stdout“ สด”


188

ฉันมีสคริปต์ง่ายๆนี้:

var exec = require('child_process').exec;

exec('coffee -cw my_file.coffee', function(error, stdout, stderr) {
    console.log(stdout);
});

ที่ฉันเพียงแค่รันคำสั่งเพื่อรวบรวมไฟล์สคริปต์กาแฟ แต่ stdout ไม่เคยถูกแสดงในคอนโซลเนื่องจากคำสั่งไม่สิ้นสุด (เนื่องจากตัวเลือก -w ของกาแฟ) หากฉันรันคำสั่งโดยตรงจากคอนโซลฉันได้รับข้อความเช่นนี้:

18:05:59 - compiled my_file.coffee

คำถามของฉันคือ: เป็นไปได้หรือไม่ที่จะแสดงข้อความเหล่านี้พร้อมกับ node.js exec? ถ้าใช่เป็นอย่างไร !

ขอบคุณ


1
ฉันมาที่นี่เพื่อค้นหา stdout จาก Python ที่สามารถเรียกใช้งานได้ โปรดทราบว่าด้านล่างทั้งหมดจะใช้งานได้ แต่คุณต้องเรียกใช้ python พร้อมกับตัวเลือก "-u" เพื่อให้การจ่ายเงินไม่ราบรื่นและมีการอัปเดตสด
Andy

คำตอบ:


266

execอย่าใช้ ใช้spawnซึ่งเป็นEventEmmiterวัตถุ จากนั้นคุณสามารถฟังstdout/ stderrเหตุการณ์ ( spawn.stdout.on('data',callback..)) ขณะที่พวกเขาเกิดขึ้น

จากเอกสาร NodeJS:

var spawn = require('child_process').spawn,
    ls    = spawn('ls', ['-lh', '/usr']);

ls.stdout.on('data', function (data) {
  console.log('stdout: ' + data.toString());
});

ls.stderr.on('data', function (data) {
  console.log('stderr: ' + data.toString());
});

ls.on('exit', function (code) {
  console.log('child process exited with code ' + code.toString());
});

exec บัฟเฟอร์เอาต์พุตและมักจะส่งคืนเมื่อคำสั่งดำเนินการเสร็จสิ้น


22
ดีมาก. FYI: ผู้ stdout / เหตุการณ์ stderr โทรกลับ 'ข้อมูล' อาร์กิวเมนต์เป็นบัฟเฟอร์เพื่อเรียกมันว่าด้วย .ToString ()
Sergel

4
สำหรับผู้ที่ไม่สามารถวางไข่ให้ทำงานบน Windows ได้ดูคำตอบที่ยอดเยี่ยมนี้
tomekwi

17
exec ยังเป็น EventEmitter อย่างน้อยก็ล่าสุด
Nikolay Tsenkov

4
โปรดทราบว่าการโทรกลับจะไม่ถูกเรียกใช้เมื่อใดก็ตามที่โปรแกรมแสดงบรรทัดใหม่ หากคุณต้องการได้รับ "เหตุการณ์" จากกระบวนการลูกกระบวนการนี้จะต้องล้างบัฟเฟอร์ ( flush(stdout);ใน C) เพื่อดำเนินการเหตุการณ์ใน Node.js
Julian F. Weinert

5
+1 บน exec ยังเป็น EventEmitter .. ใช้เวลา 2 ชั่วโมงในการ refactoring สตริงของฉันให้เป็นอาร์เรย์ args (บรรทัดคำสั่ง ffmpeg ที่ยาวและซับซ้อน) .. เพียงเพื่อจะพบว่าฉันไม่จำเป็นต้องทำจริงๆ
deadconversations

176

exec จะส่งคืนวัตถุ ChildProcess ที่เป็น EventEmitter

var exec = require('child_process').exec;
var coffeeProcess = exec('coffee -cw my_file.coffee');

coffeeProcess.stdout.on('data', function(data) {
    console.log(data); 
});

หรือpipestdout ของกระบวนการลูกไปที่ stdout หลัก

coffeeProcess.stdout.pipe(process.stdout);

หรือสืบทอด stdio โดยใช้การวางไข่

spawn('coffee -cw my_file.coffee', { stdio: 'inherit' });

35
ดูเหมือนว่าสิ่งนี้จะง่ายขึ้นเพียงแค่ใช้pipe:coffeeProcess.stdout.pipe(process.stdout);
Eric Freese

3
@ ความคิดเห็นของ EricFreese คือสิ่งที่ฉันกำลังมองหาเพราะฉันต้องการที่จะยกระดับคุณสมบัติการแทนที่ตัวละครของ stdout (ควบคุมไม้โปรแทรกเตอร์ในสคริปต์โหนด)
LoremIpsum

19
spawn(cmd, argv, { stdio: 'inherit' })เรียบง่าย: ดูnodejs.org/api/child_process.html#child_process_options_stdioสำหรับตัวอย่างอื่น
Morgan Touverey แสดงผล

3
+1 สำหรับข้อเสนอแนะ @ MorganTouvereyQuilling ที่จะใช้กับspawn stdio: 'inherit'มันผลิตออกถูกต้องกว่าexecและท่อstdout/ ยกตัวอย่างเช่นเมื่อแสดงข้อมูลความคืบหน้าจากstderr git clone
Livven

57

แล้วมีหลายคำตอบ แต่ไม่มีของพวกเขาพูดถึงสิ่งที่ดีที่สุด (และที่ง่ายที่สุด) วิธีการทำเช่นนี้ซึ่งมีการใช้spawnและตัวเลือก{ stdio: 'inherit' } git cloneดูเหมือนว่าการผลิตการส่งออกที่ถูกต้องที่สุดตัวอย่างเช่นเมื่อแสดงข้อมูลความคืบหน้าจาก

เพียงทำสิ่งนี้:

var spawn = require('child_process').spawn;

spawn('coffee', ['-cw', 'my_file.coffee'], { stdio: 'inherit' });

ให้เครดิตแก่ @MorganTouvereyQuilling สำหรับการชี้เรื่องนี้ในความคิดเห็นนี้


1
ฉันพบว่าเมื่อกระบวนการย่อยใช้ผลลัพธ์ที่จัดรูปแบบแล้วเช่นข้อความสีstdio: "inherit"รักษาการจัดรูปแบบนั้นในขณะที่child.stdout.pipe(process.stdout)ไม่
Rikki Gibson

รักษาเอาต์พุตอย่างสมบูรณ์แม้ในกระบวนการที่มีเอาต์พุตที่ซับซ้อนเช่นแถบความคืบหน้าในการติดตั้ง npm ! น่ากลัว
Dave Koo

1
ทำไมนี่ไม่ใช่คำตอบที่ยอมรับ? มันเป็นคนเดียวที่ได้ผลสำหรับฉันและมันก็แค่ 2 f * ไลน์ !!!
ลินคอล์น

เคล็ดลับนี้มีประโยชน์เมื่อเรียกใช้แอปพลิเคชันบรรทัดคำสั่ง Symfony บางตัวที่ใช้แถบความคืบหน้า ไชโย
Halfstop

นี่ควรเป็นคำตอบที่ได้รับการยอมรับ - มีเพียงสิ่งเดียวที่รักษาเอาท์พุทที่สมบูรณ์แบบและมันง่ายที่สุด? ใช่โปรด
วิวัฒนาการ

21

ฉันแค่อยากจะเพิ่มปัญหาเล็ก ๆ ข้อหนึ่งด้วยการconsole.log()เอาท์พุทสตริงบัฟเฟอร์จากโพรเซสที่กลับมาด้วยคือมันเพิ่มบรรทัดใหม่ซึ่งสามารถกระจายเอาท์พุทกระบวนการที่เกิดจากการโพสต์ไปยังบรรทัดเพิ่มเติม หากคุณส่งออกstdoutหรือstderrด้วยprocess.stdout.write()แทนที่จะเป็นconsole.log()คุณจะได้รับผลลัพธ์คอนโซลจากกระบวนการเกิด 'ตามสภาพ'

ฉันเห็นวิธีแก้ไขปัญหาที่นี่: Node.js: การพิมพ์ไปยังคอนโซลโดยไม่ขึ้นบรรทัดใหม่หรือไม่

หวังว่าจะช่วยให้ใครบางคนใช้วิธีการแก้ปัญหาข้างต้น (ซึ่งเป็นสิ่งที่ดีสำหรับการถ่ายทอดสดแม้ว่าจะมาจากเอกสารประกอบ)


1
สำหรับการใช้เอาต์พุตที่แม่นยำยิ่งขึ้นspawn(command, args, { stdio: 'inherit' })ตามที่ @MorganTouvereyQuilling แนะนำไว้ที่นี่stackoverflow.com/questions/10232192/…
Livven

21

แรงบันดาลใจจากคำตอบของ Nathanael Smith และความคิดเห็นของ Eric Freese อาจเป็นเรื่องง่ายเพียง:

var exec = require('child_process').exec;
exec('coffee -cw my_file.coffee').stdout.pipe(process.stdout);

นี้ดูเหมือนว่าจะทำงานได้ดีสำหรับคำสั่งง่ายๆเช่นแต่ล้มเหลวสำหรับคำสั่งที่ซับซ้อนมากขึ้นเช่นls npm installฉันยังลอง piping ทั้ง stdout และ stderr กับวัตถุกระบวนการที่เกี่ยวข้อง
linuxdan

@linuxdan อาจเป็นเพราะ npm กำลังเขียนเป็น stderr (ฉันเห็นบางคนเขียนแถบความคืบหน้าอยู่ที่นั่น) คุณสามารถไปป์ stderr หรือขยายโซลูชัน Tongfa เพื่อฟัง stderr
Sergiu

@linuxdan จากสิ่งที่ฉันเห็นวิธีที่น่าเชื่อถือที่สุดคือspawn(command, args, { stdio: 'inherit' })ตามที่แนะนำไว้ที่นี่stackoverflow.com/questions/10232192/…
Livven

คำตอบที่ดีที่สุดขอบคุณสำหรับสิ่งนี้ ทำงานเหมือนมนต์เสน่ห์
Abhishek Sharma

12

ฉันพบว่ามันมีประโยชน์ในการเพิ่มสคริปต์ exec ที่กำหนดเองในยูทิลิตี้ของฉันที่ทำสิ่งนี้

utilities.js

const { exec } = require('child_process')

module.exports.exec = (command) => {
  const process = exec(command)

  process.stdout.on('data', (data) => {
    console.log('stdout: ' + data.toString())
  })

  process.stderr.on('data', (data) => {
    console.log('stderr: ' + data.toString())
  })

  process.on('exit', (code) => {
    console.log('child process exited with code ' + code.toString())
  })
}

app.js

const { exec } = require('./utilities.js')

exec('coffee -cw my_file.coffee')

5

หลังจากตรวจสอบคำตอบอื่น ๆ ทั้งหมดฉันลงเอยด้วย:

function oldSchoolMakeBuild(cb) {
    var makeProcess = exec('make -C ./oldSchoolMakeBuild',
         function (error, stdout, stderr) {
             stderr && console.error(stderr);
             cb(error);
        });
    makeProcess.stdout.on('data', function(data) {
        process.stdout.write('oldSchoolMakeBuild: '+ data);
    });
}

บางครั้งdataจะมีหลายบรรทัดดังนั้นoldSchoolMakeBuildส่วนหัวจะปรากฏหนึ่งครั้งสำหรับหลายบรรทัด แต่สิ่งนี้ไม่ได้รบกวนฉันมากพอที่จะเปลี่ยนแปลง


3

child_process.spawn ส่งคืนวัตถุที่มี stdout และ stderr streams คุณสามารถแตะที่สตรีม stdout เพื่ออ่านข้อมูลที่กระบวนการลูกส่งกลับไปยังโหนด stdout เป็นสตรีมมี "data", "end" และกิจกรรมอื่น ๆ ที่สตรีมมี การวางไข่จะดีที่สุดเมื่อคุณต้องการให้กระบวนการลูกส่งคืนข้อมูลจำนวนมากไปยังโหนด - การประมวลผลภาพการอ่านข้อมูลไบนารีเป็นต้น

เพื่อให้คุณสามารถแก้ปัญหาของคุณโดยใช้ child_process.spawn ตามที่ใช้ด้านล่าง

var spawn = require('child_process').spawn,
ls = spawn('coffee -cw my_file.coffee');

ls.stdout.on('data', function (data) {
  console.log('stdout: ' + data.toString());
});

ls.stderr.on('data', function (data) {
  console.log('stderr: ' + data.toString());
});

ls.on('exit', function (code) {
  console.log('code ' + code.toString());
});

1

นี่คือฟังก์ชั่นตัวช่วย async ที่เขียนด้วยตัวพิมพ์ดีดที่ดูเหมือนจะใช้เคล็ดลับสำหรับฉัน ฉันเดาว่านี่จะไม่ทำงานสำหรับกระบวนการที่ยาวนาน แต่ยังคงมีประโยชน์สำหรับใครบางคน?

import * as child_process from "child_process";

private async spawn(command: string, args: string[]): Promise<{code: number | null, result: string}> {
    return new Promise((resolve, reject) => {
        const spawn = child_process.spawn(command, args)
        let result: string
        spawn.stdout.on('data', (data: any) => {
            if (result) {
                reject(Error('Helper function does not work for long lived proccess'))
            }
            result = data.toString()
        })
        spawn.stderr.on('data', (error: any) => {
            reject(Error(error.toString()))
        })
        spawn.on('exit', code => {
            resolve({code, result})
        })
    })
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.