Node.js는 자식 프로세스를 생성하고 터미널 출력을 라이브로 가져옵니다.


113

나는 'hi'를 출력하고, 1 초 동안 sleeps, 'hi'를 출력하고, 1 초 동안 sleeps 등을 출력하는 스크립트가 있습니다. 이제이 모델로이 문제를 해결할 수있을 것이라고 생각했습니다.

var spawn = require('child_process').spawn,
temp    = spawn('PATH TO SCRIPT WITH THE ABOVE BEHAVIOUR');

temp.stdout.pipe(process.stdout);

이제 문제는 출력을 표시하기 위해 작업을 완료해야한다는 것입니다. 내가 이해하고 있듯이 이것은 새로 생성 된 프로세스가 실행 제어를 취한다는 사실 때문입니다. 분명히 node.js는 스레드를 지원하지 않으므로 솔루션이 있습니까? 내 아이디어는 두 개의 인스턴스를 실행할 수 있는데, 첫 번째는 작업을 생성하는 특정 목적을 위해 첫 번째 인스턴스를 실행하고 이것이 달성 될 수 있다는 점을 고려하여 두 번째 인스턴스의 프로세스로 출력을 파이프하도록하는 것이 었습니다.


1
자식 프로세스가 작성되면 콘솔 출력을 버퍼링하지 않도록 플래그 python를 전달하는 것을 잊지 마십시오 -u. 그렇지 않으면 스크립트가 라이브가 아닌 것처럼 보일 것입니다. stackoverflow.com/a/49947671/906265
Aivaras

다른 것 대신 npmjs.com/package/cross-spawn 을 사용하십시오 . 더 좋습니다.
Andrew Koster

답변:


92

여전히 Node.js에 발을 담그고 있지만 몇 가지 아이디어가 있습니다. 첫째, execFile대신 사용해야한다고 생각합니다 spawn. execFile스크립트 경로가있을 때 사용하는 반면 spawnNode.js가 시스템 경로에 대해 확인할 수있는 잘 알려진 명령을 실행하기위한 것입니다.

1. 버퍼링 된 출력을 처리 하는 콜백제공합니다 .

var child = require('child_process').execFile('path/to/script', [ 
    'arg1', 'arg2', 'arg3', 
], function(err, stdout, stderr) { 
    // Node.js will invoke this callback when process terminates.
    console.log(stdout); 
});  

2. 자식 프로세스의 stdout 스트림 ( 9thport.net )에 리스너를 추가합니다.

var child = require('child_process').execFile('path/to/script', [ 
    'arg1', 'arg2', 'arg3' ]); 
// use event hooks to provide a callback to execute when data are available: 
child.stdout.on('data', function(data) {
    console.log(data.toString()); 
});

또한 생성 된 프로세스를 Node의 제어 터미널에서 분리하여 비동기 적으로 실행할 수있는 옵션이있는 것으로 보입니다. 아직 테스트하지 않았지만 API 문서 에 다음과 같은 예제가 있습니다.

child = require('child_process').execFile('path/to/script', [ 
    'arg1', 'arg2', 'arg3', 
], { 
    // detachment and ignored stdin are the key here: 
    detached: true, 
    stdio: [ 'ignore', 1, 2 ]
}); 
// and unref() somehow disentangles the child's event loop from the parent's: 
child.unref(); 
child.stdout.on('data', function(data) {
    console.log(data.toString()); 
});

8
쉘 cmd를 실행해야하므로 exec ()로이를 수행하는 방법을 설명 할 수 있다면 보너스 포인트를 얻을 수 있습니다.
DynamicDan 2014

2
당신은 사용할 수 있습니다 child.spawn()shell에 옵션을 설정 true. nodejs.org/api/…
CedX

5
또한 child.stdout을 process.stdout으로 직접 파이프 할 수도 있습니다.child.stdout.pipe(process.stdout);
darkadept

@DynamicDanjavascript let childProcess = exec ( './script-to-run --arg1 arg1value', ( error, stdout, stderror ) => { console.log( '[CALLBACK]: ' + error ); // or stdout or stderror } ); // Same as with spawn: childProcess.stdout.on ( 'data', ( data ) => { console.log( '[LIVE]: ' + data ); // Here's your live data! } );
Rik

130

이제 훨씬 쉬워졌습니다 (6 년 후)!

Spawn은 이벤트수신 할 수 있는 childObject를 반환합니다 . 이벤트는 다음과 같습니다.

  • 클래스 : ChildProcess
    • 이벤트 : '오류'
    • 이벤트 : 'exit'
    • 이벤트 : '닫기'
    • 이벤트 : '연결 끊기'
    • 이벤트 : '메시지'

childObject 에는 다음 과 같은 객체 도 있습니다.

  • 클래스 : ChildProcess
    • child.stdin
    • child.stdout
    • child.stderr
    • child.stdio
    • child.pid
    • child.connected
    • child.kill ([신호])
    • child.send (message [, sendHandle] [, 콜백])
    • child.disconnect ()

childObject에 대한 자세한 정보는 https://nodejs.org/api/child_process.html을 참조하십시오.

비동기

노드가 계속 실행될 수있는 동안 백그라운드에서 프로세스를 실행하려면 비동기 메서드를 사용하세요. 프로세스가 완료된 후 그리고 프로세스에 출력이있을 때 (예 : 스크립트의 출력을 클라이언트에 보내려는 경우) 작업을 수행하도록 선택할 수 있습니다.

child_process.spawn (...); (노드 v0.1.90)

var spawn = require('child_process').spawn;
var child = spawn('node ./commands/server.js');

// You can also use a variable to save the output 
// for when the script closes later
var scriptOutput = "";

child.stdout.setEncoding('utf8');
child.stdout.on('data', function(data) {
    //Here is where the output goes

    console.log('stdout: ' + data);

    data=data.toString();
    scriptOutput+=data;
});

child.stderr.setEncoding('utf8');
child.stderr.on('data', function(data) {
    //Here is where the error output goes

    console.log('stderr: ' + data);

    data=data.toString();
    scriptOutput+=data;
});

child.on('close', function(code) {
    //Here you can get the exit code of the script

    console.log('closing code: ' + code);

    console.log('Full output of script: ',scriptOutput);
});

콜백 + 비동기 메서드를 사용하는 방법은 다음과 같습니다 .

var child_process = require('child_process');

console.log("Node Version: ", process.version);

run_script("ls", ["-l", "/home"], function(output, exit_code) {
    console.log("Process Finished.");
    console.log('closing code: ' + exit_code);
    console.log('Full output of script: ',output);
});

console.log ("Continuing to do node things while the process runs at the same time...");

// This function will output the lines from the script 
// AS is runs, AND will return the full combined output
// as well as exit code when it's done (using the callback).
function run_script(command, args, callback) {
    console.log("Starting Process.");
    var child = child_process.spawn(command, args);

    var scriptOutput = "";

    child.stdout.setEncoding('utf8');
    child.stdout.on('data', function(data) {
        console.log('stdout: ' + data);

        data=data.toString();
        scriptOutput+=data;
    });

    child.stderr.setEncoding('utf8');
    child.stderr.on('data', function(data) {
        console.log('stderr: ' + data);

        data=data.toString();
        scriptOutput+=data;
    });

    child.on('close', function(code) {
        callback(scriptOutput,code);
    });
}

위의 방법을 사용하면 스크립트의 모든 출력 행을 클라이언트로 보낼 수 있습니다 (예 : stdout또는에서 이벤트를 수신 할 때 Socket.io를 사용하여 각 행을 전송 stderr).

동기식

노드가 수행중인 작업을 중지 하고 스크립트가 완료 될 때까지 기다리 려면 동기 버전을 사용할 수 있습니다.

child_process.spawnSync (...); (노드 v0.11.12 이상)

이 방법의 문제 :

  • 스크립트를 완료하는 데 시간이 걸리면 해당 시간 동안 서버가 중단됩니다!
  • stdout은 스크립트 실행이 완료된 후에 만 ​​반환됩니다 . 동기식이기 때문에 현재 줄이 끝날 때까지 계속할 수 없습니다. 따라서 스폰 라인이 끝날 때까지 'stdout'이벤트를 캡처 할 수 없습니다.

사용 방법:

var child_process = require('child_process');

var child = child_process.spawnSync("ls", ["-l", "/home"], { encoding : 'utf8' });
console.log("Process finished.");
if(child.error) {
    console.log("ERROR: ",child.error);
}
console.log("stdout: ",child.stdout);
console.log("stderr: ",child.stderr);
console.log("exist code: ",child.status);

11
+1, 이제 정답으로 선택해야합니다. 참고로 콜백의 데이터 변수는 Buffer 객체로 제공됩니다. child.stdout.setEncoding('utf8')utf8 문자열이 들어오고 싶다면 사용할 수 있습니다 .
Ashish

2
stdout비동기식으로 정보가 필요한 경우 , 즉 프로세스가 계속되면 나머지 프로그램이 계속되는 경우에는 작동하지 않습니다 .
Christian Hujer

2
안녕하세요 @ChristianHujer! 비동기와 동기화를 모두 포함하도록 답변을 업데이트했습니다. : D
Katie

당신은 스크립트가있는 경우 : console.log("Output 1"); console.error("Boom"); console.log("Output 2");그리고 내가 뭘 spawnAsync('node ./script.js')... 어떻게 출력의 순서를 유지합니까? 내 출력은 항상 잘못된 순서로 나오는 것 같습니다.
Bryan Ray

당신이 사용하는 경우 참고로, 그것도 쉽게 pipe또는 pipeline나에 적절한 옵션을 전달 spawn.
RichS

25

내가 찾은 가장 깨끗한 접근 방식은 다음과 같습니다.

require("child_process").spawn('bash', ['./script.sh'], {
  cwd: process.cwd(),
  detached: true,
  stdio: "inherit"
});

15
정확히 무엇을하고 있습니까? 왜 작동합니까? 이것이 더 깨끗한 접근 방식 인 이유는 무엇입니까?
건포도는

16

자식 프로세스에서 npm을 생성 할 때 "npm install"명령에서 로깅 출력을 얻는 데 약간의 문제가있었습니다. 종속성의 실시간 로깅이 상위 콘솔에 표시되지 않았습니다.

원본 포스터가 원하는 작업을 수행하는 가장 간단한 방법은 다음과 같습니다 (Windows에서 npm을 생성하고 모든 것을 부모 콘솔에 기록).

var args = ['install'];

var options = {
    stdio: 'inherit' //feed all child process logging into parent process
};

var childProcess = spawn('npm.cmd', args, options);
childProcess.on('close', function(code) {
    process.stdout.write('"npm install" finished with code ' + code + '\n');
});

3

이 기능을 자주 필요로하여 std-pour 라는 라이브러리로 패키징했습니다 . 명령을 실행하고 실시간으로 출력을 볼 수 있어야합니다. 간단히 설치하려면 :

npm install std-pour

그런 다음 명령을 실행하고 실시간으로 출력을 보는 것은 간단합니다.

const { pour } = require('std-pour');
pour('ping', ['8.8.8.8', '-c', '4']).then(code => console.log(`Error Code: ${code}`));

약속 기반이므로 여러 명령을 연결할 수 있습니다. 기능 시그니처와도 호환 child_process.spawn되므로 사용하는 곳 어디에서나 대체 할 수 있습니다.


1
@KodieGrantham 그것이 당신을 위해 일하고있어 기쁩니다! 멋진 일을하고있는 것 같아서 계속 뛰길 바랍니다.
Joel B

1

아이:

setInterval(function() {
    process.stdout.write("hi");
}, 1000); // or however else you want to run a timer

부모의:

require('child_process').fork('./childfile.js');
// fork'd children use the parent's stdio

1

PHP와 유사한 패스 스루

import { spawn } from 'child_process';

export default async function passthru(exe, args, options) {
    return new Promise((resolve, reject) => {
        const env = Object.create(process.env);
        const child = spawn(exe, args, {
            ...options,
            env: {
                ...env,
                ...options.env,
            },
        });
        child.stdout.setEncoding('utf8');
        child.stderr.setEncoding('utf8');
        child.stdout.on('data', data => console.log(data));
        child.stderr.on('data', data => console.log(data));
        child.on('error', error => reject(error));
        child.on('close', exitCode => {
            console.log('Exit code:', exitCode);
            resolve(exitCode);
        });
    });
}

용법

const exitCode = await passthru('ls', ['-al'], { cwd: '/var/www/html' })

0

child_process.exec나도 라이브 피드백이 필요했기 때문에 관련 답변을 추가 하고 스크립트가 끝날 때까지 아무것도 얻지 못했습니다. 이것은 또한 받아 들인 대답에 대한 내 의견을 보완하지만 형식이 지정되면 조금 더 이해하기 쉽고 읽기 쉽습니다.

기본적으로 Gulp를 호출하는 npm 스크립트가 있으며 이후 child_process.execOS에 따라 bash 또는 배치 스크립트를 실행하는 데 사용하는 작업을 호출합니다 . 두 스크립트 모두 Gulp를 통해 빌드 프로세스를 실행 한 다음 Gulp 출력과 함께 작동하는 일부 바이너리를 호출합니다.

다른 것 (스폰 등)과 똑같지 만 완료를 위해 정확히 수행하는 방법은 다음과 같습니다.

// INCLUDES
import * as childProcess from 'child_process'; // ES6 Syntax


// GLOBALS
let exec = childProcess.exec; // Or use 'var' for more proper 
                              // semantics, though 'let' is 
                              // true-to-scope


// Assign exec to a variable, or chain stdout at the end of the call
// to exec - the choice, yours (i.e. exec( ... ).stdout.on( ... ); )
let childProcess = exec
(
    './binary command -- --argument argumentValue',
    ( error, stdout, stderr ) =>
    {
        if( error )
        {
            // This won't show up until the process completes:
            console.log( '[ERROR]: "' + error.name + '" - ' + error.message );
            console.log( '[STACK]: ' + error.stack );

            console.log( stdout );
            console.log( stderr );
            callback();            // Gulp stuff
            return;
        }

        // Neither will this:
        console.log( stdout );
        console.log( stderr );
        callback();                // Gulp stuff
    }
);

이제 이벤트 리스너를 추가하는 것만 큼 간단합니다. 대상 stdout:

childProcess.stdout.on
(
    'data',
    ( data ) =>
    {
        // This will render 'live':
        console.log( '[STDOUT]: ' + data );
    }
);

그리고 stderr:

childProcess.stderr.on
(
    'data',
    ( data ) =>
    {
        // This will render 'live' too:
        console.log( '[STDERR]: ' + data );
    }
);

전혀 나쁘지 않음-HTH

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.