質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.11%

Node.jsからシェルスクリプトを実行して標準入力でデータを渡したい

解決済

回答 1

投稿

  • 評価
  • クリップ 1
  • VIEW 2,304
退会済みユーザー

退会済みユーザー

前提・実現したいこと

pythonからシェルスクリプトを実行するコードをNode.jsで書き直すことになりました。
シェルスクリプトには標準入力でJSONを渡しています。
以下のページを参考にしました。
https://nodejs.org/docs/latest-v8.x/api/child_process.html#child_process_child_process_spawn_command_args_options
https://nodejs.org/docs/latest-v8.x/api/stream.html#stream_writable_write_chunk_encoding_callback

発生している問題・エラーメッセージ

標準入力を渡さない場合はシェルスクリプトが意図した通りに実行されました。
標準入力でシェルスクリプトにデータを渡そうとすると上手くいかず、下記のエラーメッセージが表示されるようになりました。
なぜか偶にエラーにならず実行されることもありますが、その場合はシェル側で標準入力が取得できず、標準入力を渡さず実行した場合と同じ結果になりました。

events.js:183
    throw er; // Unhandled 'error' event
    ^

Error: write EPIPE
    at _errnoException (util.js:1022:11)
    at WriteWrap.afterWrite [as oncomplete] (net.js:867:14)

該当のソースコード

$ node test.jsで実行しています。

// test.js
const path = require('path')
const spawn = require('child_process').spawn

const script = path.resolve(__dirname, 'path', 'to', 'script')
const data = {
    name: 'foo',
    age: 999
}

const child = spawn(script)
child.stdin.write(JSON.stringfy(data), 'utf8')
child.stdin.end()

ちなみにpythonでは以下のようなコードで、意図した通りの動作が確認できました。

from os import path
import subprocess
import json

SCRIPT = path.join(
    path.dirname(path.abspath(__file__)),
    'path', 'to', 'script'
)
DATA = {
    name: 'foo',
    age: 999
}

subprocess.run(
    [SCRIPT],
    input=json.dumps(DATA).encode('utf-8'),
    check=True
)

シェルスクリプトは以下のような感じです。直接実行してもpythonから実行しても問題ないので標準入力の受け取り方に問題があるとは思えませんが、念のため載せておきます。

#!/bin/bash

if [ ! -p /dev/stdin ]; then
    # 標準入力から何も渡ってこない場合はエラー
    exit 1
fi

# 実行したい処理
do_something -r '...' < /dev/stdin | # 続く...

試したこと

child.stdin.write()の戻り値を調べるとtrueでした。callbackも引数に渡すと、それが実行された上で上記のエラーが発生するようです。
spawn(script)spawn('cat') にしてchild.stdout.on('data', data => console.log(data))で標準出力を表示したら、エラーは発生せずに標準入力に渡したJSONがそのまま表示できました。

補足情報(言語/FW/ツール等のバージョンなど)

OS: Ubuntu16.04
Node.js: v8.9.4

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

check解決した方法

0

catに標準入力を流すことは出来ていたので、シェルスクリプトでの標準入力の受け取り方を以下のように修正しました。

-if [ ! -p /dev/stdin ]; then
-    # 標準入力から何も渡ってこない場合はエラー
-    exit 1
-fi

 # 実行したい処理
-do_something -r '...' < /dev/stdin | # 続く...
+cat | do_something -r '...' | # 続く...


どうやらシェルスクリプトの中で/dev/stdinを扱おうとすると問題があるみたいで、リダイレクトで読み込めないだけでなくガード節でテストするだけでも上述のエラーが発生してしまうようです。

一応解決策が見つかりましたが、なぜnodeから実行すると/dev/stdinがダメなのか分かる方は教えていただけると有難いです。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 90.11%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる