Node.jsは子プロセスを生成し、ターミナル出力をライブで取得します


113

「hi」を出力し、1秒間スリープし、「hi」を出力し、1秒間スリープする、などのスクリプトがあります。今、私はこのモデルでこの問題に取り組むことができると思いました。

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

temp.stdout.pipe(process.stdout);

ここで問題は、出力を表示するためにタスクを完了する必要があることです。私が理解しているように、これは新しく生成されたプロセスが実行制御を取得するという事実によるものです。明らかにnode.jsはスレッドをサポートしていないので、ソリューションはありますか?私の考えは、2つのインスタンスを実行することでした。1つはタスクを作成する特定の目的で、もう1つは2番目のインスタンスのプロセスに出力をパイプすることでした。


1
子プロセスが記述されている場合python、次に渡すことを忘れないでください-u、それ以外の場合は、スクリプトのようになります、それはコンソール出力をバッファリングしないようにするためのフラグは、ライブではありません stackoverflow.com/a/49947671/906265
Aivaras

他のものの代わりにnpmjs.com/package/cross-spawnを使用してください。それだけです。
Andrew Koster

回答:


92

私はまだNode.jsで足を濡らしていますが、いくつかのアイデアがあります。まず、のexecFile代わりに使用する必要があると思いますspawnexecFileスクリプトへのパスがある場合に使用しますが、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()); 
});

さらに、生成されたプロセスをノードの制御ターミナルから切り離すことができるオプションがあるように見えます。これにより、非同期に実行できます。これはまだテストしていませんが、次のような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に設定オプションtruenodejs.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] [、callback])
    • 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文字列が入ってくるしたい場合。
アシシュ

2
これは、stdout非同期の情報が必要な場合、つまり、プロセスが続行する場合、残りのプログラムが続行する場合は機能しません。
Christian Hujer、

2
@ChristianHujerさん、こんにちは。非同期と同期の両方を含めるように回答を更新しました:D
Katie

次のようなスクリプトがある場合:console.log("Output 1"); console.error("Boom"); console.log("Output 2");そして私はやっていspawnAsync('node ./script.js')ます...出力の順序をどのように保存しますか?私の出力は常に間違った順序で出力されるようです。
ブライアンレイ

参考までに、適切なオプションを使用する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うまくいきました!Y'allはあなたがクールな仕事をしているように見えます。
ジョエル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.exec、OSに応じてbashまたはバッチスクリプトを実行するために後で使用するタスクを呼び出します。どちらのスクリプトもGulpを介してビルドプロセスを実行し、Gulp出力で機能するバイナリをいくつか呼び出します。

それは他のもの(spawnなど)とまったく同じですが、完了のために、ここでそれを行う方法を正確に示します:

// 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.