Exec: відображення відображення "в прямому ефірі"


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", щоб зробити outout небуферованим і тим самим мати оновлення в реальному часі.
Енді

Відповіді:


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 "data" є буфером, тому називайте його з .toString ()
SergeL

4
Тим із вас, хто не може отримати нерест для роботи в Windows, погляньте на цю чудову відповідь .
tomekwi

17
exec також є EventEmitter, принаймні, в останній час.
Микола Ценков

4
Також пам’ятайте, що зворотний виклик не буде викликаний, коли програма виводить новий рядок. Якщо ви хочете отримувати "події" від дочірнього процесу, цей процес повинен змити буфер ( flush(stdout);в С), щоб запустити події в Node.js.
Julian F. Weinert

5
+1 на exec також будучи EventEmitter .. витратив 2 години на рефакторинг моєї рядка в масив args (дуже довгий і складний командний рядок ffmpeg) .. тільки щоб з'ясувати, що мені насправді не потрібно.
мертві розмови

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); 
});

АБО pipeдочірній процес виводить на основний відтінок.

coffeeProcess.stdout.pipe(process.stdout);

АБО спадкуйте stdio за допомогою нересту

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

35
Схоже, це можна спростити, просто скориставшись pipe:coffeeProcess.stdout.pipe(process.stdout);
Ерік Фрізе

3
@ Коментар EricFreese - це те, що я шукав, тому що я хотів використовувати функцію заміни символів stdout (використовуючи транспортир у сценарії вузла)
LoremIpsum

19
Simpler: spawn(cmd, argv, { stdio: 'inherit' }). Дивіться nodejs.org/api/child_process.html#child_process_options_stdio для різних прикладів.
Morgan Touverey Quilling

3
+1 за @ пропозицію MorganTouvereyQuilling для використання spawnз stdio: 'inherit'. Він дає більш точний вихід, ніж execта трубопровід stdout/ stderr, наприклад, при відображенні інформації про хід від a git clone.
Livven

57

Відповідей уже є кілька, проте жоден з них не згадує найкращий (і найпростіший) спосіб зробити це, який використовується spawnі { stdio: 'inherit' }варіант . Це здається, що дає найбільш точний результат, наприклад, коли відображається інформація про хід від a 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)це не відбувається.
Рікі Гібсон

Це чудово зберігає вихід навіть у процесах зі складним виведенням, таких як смуги прогресу при встановленні npm. Дивовижно!
Дейв Куо

1
чому це не прийнята відповідь? це був єдиний, який працював на мене, і це всього 2 f * рядки !!!
Лінкольн

Ця порада була корисною при виконанні деяких програм командного рядка Symfony, які використовують панелі прогресу. Ура.
Полустоп

Це має бути прийнята відповідь - лише те, що зберігає ідеальне представлення виводу, і це найпростіше? так, будь ласка
evnp

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

Натхненний відповіддю Натанаїла Сміта та коментарем Еріка Фрізе, це може бути так само просто:

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

Це, здається, працює добре для простих команд, як, lsале не вдається для більш складних команд, таких як npm install. Я навіть спробував підключити як stdout, так і stderr до відповідних об'єктів процесу.
linuxdan

@ linuxdan це може бути тому, що npm пише в stderr (я бачив, як деякі пишуть там смужку прогресу). ви можете передавати також більш stderr або розширити рішення Tongfa для прослуховування на stderr.
Сергіу

@linuxdan З того, що я бачив найнадійніший спосіб spawn(command, args, { stdio: 'inherit' }), як запропоновано тут stackoverflow.com/questions/10232192 / ...
Livven

Найкраща відповідь, дякую за це. Працював як шарм
Абхішек Шарма

12

Мені здається корисним додати користувальницький скрипт до моїх утиліт, які це роблять.

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. Ви можете натиснути на потік stdout, щоб прочитати дані, які дочірній процес надсилає назад до Node. stdout, який є потоком, має "дані", "кінець" та інші події, які мають потоки. ікру найкраще використовувати, коли ви хочете, щоб дочірній процес повертав велику кількість даних у Node - обробка зображень, читання двійкових даних тощо.

тож ви можете вирішити свою проблему, використовуючи 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

Ось функція помічника асинхронізації, написана машинописом, яка, здається, робить для мене трюк. Я думаю, це не буде працювати для довготривалих процесів, але все-таки може бути корисним для когось?

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})
        })
    })
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.