2014-12-13 7 views
11

Muszę uruchomić jakieś interaktywne polecenie powłoki wewnątrz node.js. Pozwala nasza interaktywna powłoka być $ python:Jak uruchomić interaktywną komendę powłoki wewnątrz pliku node.js?

var cp = require('child_process'); 
var pythonChildProcess = cp.spawn('python'); 

pythonChildProcess.stdout.on("data", function(data) { 
    console.log('data successfully written!', data); // never outputs anything 
}); 

pythonChildProcess.stdin.write('1 + 1'); 
pythonChildProcess.stdin.end(); 

Kod ten nie emituje nic (ale powinny być 2 stdout).

Ale jeśli tak, pojawi się inny problem: jak uczynić go interaktywnym? Proces kończy się, gdy zadzwonię pod numer pythonChildProcess.stdin.end();! Ale po prostu chciałem zakończyć stdin i napisać dalej stdin!

UPD: Gdybym mógł emulować naciśnięcie przycisku enter - byłbym w stanie interaktywnie napisać do procesu. Ale dodanie \n na końcu wejściowego łańcucha nie pomoże.

+0

"do końca standardowego wejścia stdin i napisać następny"? Czy możesz wyjaśnić, co masz na myśli? –

+0

@ E_net4, chciałbym wykonać komendę '$ python', napisać jakieś dane wejściowe (na przykład' 1 + 1'), "nacisnąć klawisz' enter' ", pobrać trochę danych wyjściowych (' 2' w przypadku), następnie napisać kolejne wejście ('10 + 10')," naciśnij 'enter'" ponownie, uzyskaj jakieś inne wyjście, następnie wpisz inne dane itd. I takie sekwencje "wejść" nazwałem "stdins" –

+0

tak naprawdę są pojedynczy strumień stdin, do którego chcesz wysłać wiele linii wejściowych. –

Odpowiedz

4

Przede wszystkim jedną z rzeczy, które uniemożliwiają węzłowi komunikację z innymi interaktywnymi powłokami, jest to, że aplikacja podrzędna musi zachowywać swoje "interaktywne" zachowanie, nawet gdy stdin nie wygląda jak terminal. python tutaj wiedział, że jego stdin nie był terminalem, więc odmówił pracy. Można to przesłonić, dodając flagę -i do polecenia Pythona.

Po drugie, jak dobrze wspomniano w aktualizacji, zapomniałeś napisać nowy znak linii do strumienia, więc program zachowywał się tak, jakby użytkownik nie wcisnął Enter. Tak, to jest właściwa droga, ale brak trybu interaktywnego uniemożliwił uzyskanie jakichkolwiek wyników.

Oto, co możesz zrobić, aby wysłać wiele wejść do powłoki interaktywnej, wciąż będąc w stanie pobrać każdy wynik jeden po drugim. Ten kod będzie odporny na długie wyjścia, gromadząc je do momentu otrzymania pełnej linii przed wykonaniem innej instrukcji. Wiele instrukcji może być wykonywanych jednocześnie, co może być lepsze, jeśli nie zależą one od stanu procesu nadrzędnego. Możesz eksperymentować z innymi asynchronicznymi strukturami, aby osiągnąć swój cel.

var cp = require('child_process'); 
var childProcess = cp.spawn('python', ['-i']); 

childProcess.stdout.setEncoding('utf8') 

var k = 0; 
var data_line = ''; 

childProcess.stdout.on("data", function(data) { 
    data_line += data; 
    if (data_line[data_line.length-1] == '\n') { 
    // we've got new data (assuming each individual output ends with '\n') 
    var res = parseFloat(data_line); 
    data_line = ''; // reset the line of data 

    console.log('Result #', k, ': ', res); 

    k++; 
    // do something else now 
    if (k < 5) { 
     // double the previous result 
     childProcess.stdin.write('2 * + ' + res + '\n'); 
    } else { 
     // that's enough 
     childProcess.stdin.end(); 
    } 
    } 
}); 


childProcess.stdin.write('1 + 0\n'); 
3

Tl; dr wersja odpowiedzi @ E_net4 dla tych, którzy rozumieją po prostu czytając kod. Aby uzyskać szczegółowe wyjaśnienie, przeczytaj jego odpowiedź. Opisał to dobrze.

var spawn = require('child_process').spawn 

var p = spawn('node',['-i']); 

p.stdout.on('data',function (data) { 
    console.log(data.toString()) 
}); 

p.stdin.write('1 + 0\n'); 

wyjściowa:

> 
1 
Powiązane problemy