Как вы нажимаете длинную строку на /dev/stdin через child_process.spawn() в Node.js?
Я пытаюсь выполнить Inkscape, передавая данные через stdin
. Inkscape поддерживает это только через /dev/stdin
. В основном, я пытаюсь сделать что-то вроде этого:
echo "<sgv>...</svg>" | inkscape -z -f /dev/stdin -A /dev/stdout
Я не хочу писать SVG на диск.
Я просто использовал stdin.write()
, но он не работает (возможно, из-за /dev/stdin
):
var cmd = spawn("inkscape", ["-z", "-f", "/dev/stdin", "-A", "/dev/stdout"], {encoding: "buffer", stdio: ["pipe", stdoutPipe, "pipe"]});
cmd.stdin.write(svg);
Это работает, но я должен написать SVG на диск:
var cmd = spawn("inkscape", ["-z", "-f", "/dev/stdin", "-A", "/dev/stdout"], {encoding: "buffer", stdio: [fs.openSync('file.svg', "a"), stdoutPipe, "pipe"]});
Я попытался передать поток в stdio
, но я просто продолжаю получать TypeError: Incorrect value for stdio stream: [object Object]
Любые идеи?
Добавление
В примерах используется Inkscape, но мой вопрос применим к любой произвольной программе, используя /dev/stdin
.
Кстати, это сработало бы для меня:
var exec = require('child_process').exec;
exec("echo \"<svg>...</svg>\" | inkscape -z -f /dev/stdin -A /dev/stdout | cat", function (error, stdout, stderr) {});
Кроме того, мой SVG слишком длинный, поэтому он выдает ошибку: Error: spawn Unknown system errno 7
Ответы
Ответ 1
Итак, я разобрался с работой. Это кажется немного взломанным, но все работает отлично.
Сначала я сделал одну оболочку оболочки script:
cat | inkscape -z -f /dev/stdin -A /dev/stdout | cat
Затем я просто создаю этот файл и пишу в stdin следующим образом:
cmd = spawn("shell_script");
cmd.stdin.write(svg);
cmd.stdin.end();
cmd.stdout.pipe(pipe);
Я действительно думаю, что это должно работать без оболочки script, но это не будет (для меня, по крайней мере). Это может быть ошибка Node.js.
Ответ 2
Хорошо, у меня нет Inkscape, но это, похоже, решает проблему Node.js. Я использую wc
как стенд в Inkscape; параметр -c
просто выводит количество байтов в данном файле (в данном случае /dev/stdin
).
var child_process = require('child_process');
/**
* Create the child process, with output piped to the script stdout
*/
var wc = child_process.spawn('wc', ['-c', '/dev/stdin']);
wc.stdout.pipe(process.stdout);
/**
* Write some data to stdin, and then use stream.end() to signal that we're
* done writing data.
*/
wc.stdin.write('test');
wc.stdin.end();
Трюк, похоже, сигнализирует о том, что вы закончили запись в поток. В зависимости от того, насколько велика ваша SVG, вам может потребоваться обратить внимание на противодавление от Inkscape, обработав событие 'drain'
.
Что касается передачи потока в вызов child_process.spawn
, вам вместо этого нужно использовать опцию 'pipe'
, а затем передать читаемый поток в child.stdin
, как показано ниже. Я знаю, что это работает в Node v0.10.26, но не уверен до этого.
var stream = require('stream');
var child_process = require('child_process');
/**
* Create the child process, with output piped to the script stdout
*/
var wc = child_process.spawn('wc', ['-c', '/dev/stdin'], {stdin: 'pipe'});
wc.stdout.pipe(process.stdout);
/**
* Build a readable stream with some data pushed into it.
*/
var readable = new stream.Readable();
readable._read = function noop() {}; // See note below
readable.push('test me!');
readable.push(null);
/**
* Pipe our readable stream into wc standard input.
*/
readable.pipe(wc.stdin);
Очевидно, что этот метод немного сложнее, и вы должны использовать вышеописанный метод, если у вас нет веских оснований (вы эффективно реализуете свою собственную читаемую строку).
Примечание. Функция readable._push
должна быть реализована в соответствии с документами, но необязательно что-то делать.
Ответ 3
Проблема возникает из-за того, что файловые дескрипторы в node являются сокетами и что Linux (и, вероятно, большинство Unices) не позволит вам открывать /dev/stdin, если это сокет.
Я нашел это объяснение bnoordhuis на https://github.com/nodejs/node-v0.x-archive/issues/3530#issuecomment-6561239
Данное решение близко к ответу @nmrugg:
var run = spawn("sh", ["-c", "cat | your_command_using_dev_stdin"]);
После дальнейшей работы вы можете теперь использовать модуль https://www.npmjs.com/package/posix-pipe, чтобы убедиться, что процесс видит stdin, который не является сокетом.
посмотрите на тест "должен передать данные в дочерний процесс" в этом модуле, который сводится к
var p = pipe()
var proc = spawn('your_command_using_dev_stdin', [ .. '/dev/stdin' .. ],
{ stdio: [ p[0], 'pipe', 'pipe' ] })
p[0].destroy() // important to avoid reading race condition between parent/child
proc.stdout.pipe(destination)
source.pipe(p[1])
Ответ 4
Как Ошибка Inkscape 171016 указывает, что Inkscape не поддерживает импорт через stdin, но он находится в их списке пожеланий.