node.js executa o comando do sistema de forma síncrona


171

Eu preciso na função node.js

result = execSync('node -v');

que executará de forma síncrona a linha de comando especificada e retornará todos os valores padrão desse texto de comando.

ps. A sincronização está errada. Eu sei. Apenas para uso pessoal.

ATUALIZAR

Agora temos a solução do mgutz, que nos dá o código de saída, mas não o stdout! Ainda esperando por uma resposta mais precisa.

ATUALIZAR

mgutz atualizou sua resposta e a solução está aqui :)
Além disso, como dgo.a mencionado, há um módulo autônomo exec-sync

ATUALIZAÇÃO 30-07-2014

A lib do ShellJS chegou. Considere que esta é a melhor escolha por enquanto.


ATUALIZAÇÃO 10-02-2015

FINALMENTE! O NodeJS 0.12 suporta execSyncnativamente.
Veja documentos oficiais


26
não se deixe enganar, a sincronização não está errada ... Mesmo no NodeJS todo o seu código é executado de forma síncrona, a menos que você chame explicitamente um método assíncrono ... se tudo fosse feito da maneira assíncrona, nada seria feito. Além disso, preferir métodos assíncronos não significa que seu longo cálculo não bloqueie o servidor. é uma escolha. que os criadores do Node optaram por fornecer métodos de sistema de arquivos síncronos juntamente com os assíncronos apenas mostra que também há um lugar para eles.
flow

2
Onde podemos encontrar a "biblioteca de emulação de shell Unix" de que você está falando?
Florian

@Florian ele quer dizer ShellJS
xst

Respostas:


153

O Node.js (desde a versão 0.12 - por um tempo) suporta execSync:

child_process.execSync(command[, options])

Agora você pode fazer isso diretamente:

const execSync = require('child_process').execSync;
code = execSync('node -v');

e fará o que você espera. (O padrão é canalizar os resultados de E / S para o processo pai). Observe que você também pode spawnSyncagora.


6
após 10 horas de desespero. Obrigado Dude
Tom Dev

Como posso me desconectar deste subprocesso?
precisa saber é o seguinte


54

Veja a biblioteca execSync .

É bastante fácil fazer isso com o node-ffi . Eu não recomendaria para processos de servidor, mas para utilitários de desenvolvimento geral, isso é feito. Instale a biblioteca.

npm install node-ffi

Script de exemplo:

var FFI = require("node-ffi");
var libc = new FFI.Library(null, {
  "system": ["int32", ["string"]]
});

var run = libc.system;
run("echo $USER");

[EDIT junho 2012: como obter STDOUT]

var lib = ffi.Library(null, {
    // FILE* popen(char* cmd, char* mode);
    popen: ['pointer', ['string', 'string']],

    // void pclose(FILE* fp);
    pclose: ['void', [ 'pointer']],

    // char* fgets(char* buff, int buff, in)
    fgets: ['string', ['string', 'int','pointer']]
});

function execSync(cmd) {
  var
    buffer = new Buffer(1024),
    result = "",
    fp = lib.popen(cmd, 'r');

  if (!fp) throw new Error('execSync error: '+cmd);

  while(lib.fgets(buffer, 1024, fp)) {
    result += buffer.readCString();
  };
  lib.pclose(fp);

  return result;
}

console.log(execSync('echo $HOME'));

2
Como você realmente conseguiria enviar alguma coisa stdoutdisso? Tudo o que posso obter é o código de saída do processo
Mark Kahn

@ lobos: Eu acho que assíncrono seria melhor isso. ( Resposta de Ivo )
pvorb

@pvorb - sim, exceto quando você não pode usar assíncrono :)
Mark Kahn

1
Existem razões válidas para não usar o martelo assíncrono para cada prego. Por exemplo, os mecanismos de modelo são assíncronos no Express 3 e as funções auxiliares (locais) precisam ser síncronas. E se essas funções auxiliares precisarem compilar Menos arquivos de forma assíncrona em tempo real?
mgutz

8
Eu me pergunto por que esse simples execSyncnão faz parte child_process. Eu acho que deveria ser.
Michael Härtl

31

Use o módulo ShellJS .

função exec sem fornecer retorno de chamada.

Exemplo:

var version = exec('node -v').output;

2
Observe que, no momento da redação deste documento, os documentos mencionam que o síncrono exec()consome muita CPU para processos longos.
Aram Kocharyan

1
opções, {silent: true} é a chave #
Nick

1
Isso faz algo (além de fornecer sintaxe mais curta) isso não acontece? const execSync = require('child_process').execSync; code = execSync('node -v');
user2503764

23

Existe um excelente módulo para controle de fluxo no node.js chamado asyncblock . Se a quebra do código em uma função for boa para o seu caso, o seguinte exemplo pode ser considerado:

var asyncblock = require('asyncblock');
var exec = require('child_process').exec;

asyncblock(function (flow) {
    exec('node -v', flow.add());
    result = flow.wait();
    console.log(result);    // There'll be trailing \n in the output

    // Some other jobs
    console.log('More results like if it were sync...');
});

1
Ele perguntou explicitamente sobre a versão de sincronização, não sobre as bibliotecas de fluxo.
Alexey Petrushin

22
@AlexeyPetrushin Todas as perguntas aqui são sobre um objetivo, não sobre uma maneira específica de alcançá-lo. Obrigado por votar no entanto.
nab

1
Além disso, esta é uma resposta muito útil para usuários do Windows; instalar exec-syncou ffino Windows tem uma sobrecarga enorme (VC ++, SDKs, Python, etc), mas isso é mais leve.
Mendhak

10

Isso não é possível no Node.js, ambos child_process.spawnechild_process.exec foram criados a partir do zero para serem assíncronos.

Para detalhes, consulte: https://github.com/ry/node/blob/master/lib/child_process.js

Se você realmente deseja ter esse bloqueio, coloque tudo o que precisa acontecer posteriormente em um retorno de chamada ou crie sua própria fila para lidar com isso de maneira bloqueadora, suponho que você possa usar o Async.js para esta tarefa.

Ou, caso você tenha muito tempo para gastar, entre no Node.js por conta própria.


12
estranho porque o módulo do sistema de arquivos possui chamadas síncronas. Por que não executar também?
Alfred

4
@ Alfred As chamadas FS de sincronização estão lá principalmente para carregar configurações no início do programa.
Ivo Wetzel

3
@IvoWetzel - tisk tisk ... não aprendemos a nunca dizer que algo é impossível? ;) veja minha solução abaixo.
Marcus Papa

1
@IvoWetzel "As chamadas de sincronização do FS ..." - certo, e às vezes você deseja, digamos, emitir um comando para compilar algo no início do programa e continuar na conclusão. - considerando que existem chamadas de sincronização do FS, sem um executor de sincronização parece um descuido. sou a favor de assíncrono, mas síncrono tem seus profissionais e casos de uso. deve usá-lo de maneira criteriosa, é claro.
flow

O assíncrono é bom, mas se o WidgetB depender dos resultados finais do WidgetA, todo o assíncrono do mundo não fará o trabalho. Às vezes, os processos precisam ser síncronos. Tente cozinhar de forma assíncrona. ;)
Lloyd Sargent

9

Esta é a maneira mais fácil que encontrei:

exec-Sync : https://github.com/jeremyfa/node-exec-sync
(Não confunda com execSync.)
Execute o comando shell de forma síncrona. Use isso para scripts de migração, programas cli, mas não para código de servidor comum.

Exemplo:

var execSync = require('exec-sync');   
var user = execSync('echo $USER');
console.log(user);


5

Você pode conseguir isso usando fibras. Por exemplo, usando minha biblioteca Common Node , o código ficaria assim:

result = require('subprocess').command('node -v');

3

Eu me acostumei a implementar "synchronous"coisas no final da função de retorno de chamada. Não é muito bom, mas funciona. Se você precisar implementar uma sequência de execuções de linha de comando, precisará envolver execem alguma função nomeada e chamá-la recursivamente. Esse padrão parece ser útil para mim:

SeqOfExec(someParam);

function SeqOfExec(somepParam) {
    // some stuff
    // .....
    // .....

    var execStr = "yourExecString";
    child_proc.exec(execStr, function (error, stdout, stderr) {
        if (error != null) {
            if (stdout) {
                throw Error("Smth goes wrong" + error);
            } else {
                // consider that empty stdout causes
                // creation of error object
            }
        }
        // some stuff
        // .....
        // .....

        // you also need some flag which will signal that you 
        // need to end loop
        if (someFlag ) {
            // your synch stuff after all execs
            // here
            // .....
        } else {
            SeqOfExec(someAnotherParam);
        }
    });
};

3

Eu tive um problema semelhante e acabei escrevendo uma extensão de nó para isso. Você pode conferir o repositório git. É de código aberto, gratuito e todas essas coisas boas!

https://github.com/aponxi/npm-execxi

ExecXI é uma extensão de nó escrita em C ++ para executar comandos do shell um por um, produzindo a saída do comando no console em tempo real. Maneiras encadeadas e desencadeadas opcionais estão presentes; o que significa que você pode optar por parar o script após a falha de um comando (encadeado) ou continuar como se nada tivesse acontecido!

As instruções de uso estão no arquivo Leia-me . Sinta-se livre para fazer solicitações pull ou enviar problemas!

EDIT: No entanto, ele ainda não retorna o stdout ... Apenas gera em tempo real. Faz agora.Acabei de lançar hoje. Talvez possamos construir sobre isso.

Enfim, achei que valeria a pena mencionar.


É exatamente isso que eu tenho procurado. Obrigado por dedicar tempo para fazer isso.
thealfreds

A única coisa que eu não descobri e implementei é a configuração de compilação para diferentes versões do nodejs. Acho que o escrevi no nó 0.8 ( travis-ci.org/aponxi/npm-execxi/builds/5248535 ), desde que seja npm installbem-sucedido (em outras palavras, quando o plug-in é compilado), então é bom começar a produção. Além de direcionar diferentes versões do nodejs, eu diria que está corrigido para produção ou que é bastante estável. Se houver algum erro você pode enviar uma solicitação de recebimento ou publicar um problema no github :)
Logan

1

você pode executar operações shell síncronas no nodejs da seguinte maneira:

var execSync = function(cmd) {

    var exec  = require('child_process').exec;
    var fs = require('fs');

    //for linux use ; instead of &&
    //execute your command followed by a simple echo 
    //to file to indicate process is finished
    exec(cmd + " > c:\\stdout.txt && echo done > c:\\sync.txt");

    while (true) {
        //consider a timeout option to prevent infinite loop
        //NOTE: this will max out your cpu too!
        try {
            var status = fs.readFileSync('c:\\sync.txt', 'utf8');

            if (status.trim() == "done") {
                var res = fs.readFileSync("c:\\stdout.txt", 'utf8');
                fs.unlinkSync("c:\\stdout.txt"); //cleanup temp files
                fs.unlinkSync("c:\\sync.txt");
                return res;
            }
        } catch(e) { } //readFileSync will fail until file exists
    }

};

//won't return anything, but will take 10 seconds to run
console.log(execSync("sleep 10")); 

//assuming there are a lot of files and subdirectories, 
//this too may take a while, use your own applicable file path
console.log(execSync("dir /s c:\\usr\\docs\\"));

EDIT - este exemplo é para ambientes Windows, ajuste para suas próprias necessidades de Linux, se necessário


Sim, e o mais rápido que sua CPU pode invocar ... Mas ei, quando você "precisa" de fazer algo mau, Satanás é seu homem, certo?
Marcus Pope

ok, isso é puro mal, mas incrível. Eu precisava disso para lidar com um evento do sistema de arquivos browserify.on ('register'), que não tinha retorno de chamada. Salvou o meu dia!
Robert Gould

você pode explicar por que isso funciona em mecanismos javascript? o loop while não deve ser executado infinitamente no mesmo tick enquanto o exec é executado no próximo?
badunk

Maximizar a CPU em espera ocupada é um projeto ruim.
Louis

@ Louis-DominiqueDubeau Claro, mas não há realmente nenhuma alternativa que não dependa de uma fonte de terceiros que possa ou não ser compatível com várias plataformas. Também não é uma verdadeira maximização da CPU, porque o sistema operacional não dará total prioridade ao processo nodejs. Acho que implementações de sincronização de shell ops estão no horizonte ou talvez já estejam aqui.
Marcus Papa

1

Na verdade, tive uma situação em que precisava executar vários comandos um após o outro a partir de um script de pré-instalação package.json de uma maneira que funcionasse no Windows e Linux / OSX, por isso não podia confiar em um módulo não essencial.

Então é isso que eu criei:

#cmds.coffee
childproc = require 'child_process'

exports.exec = (cmds) ->
  next = ->
    if cmds.length > 0
      cmd = cmds.shift()
      console.log "Running command: #{cmd}"
      childproc.exec cmd, (err, stdout, stderr) ->
        if err? then console.log err
        if stdout? then console.log stdout
        if stderr? then console.log stderr
        next()
    else
      console.log "Done executing commands."

  console.log "Running the follows commands:"
  console.log cmds
  next()

Você pode usá-lo assim:

require('./cmds').exec ['grunt coffee', 'nodeunit test/tls-config.js']

EDIT: como indicado, isso realmente não retorna a saída ou permite que você use o resultado dos comandos em um programa Node. Uma outra idéia para isso é usar backcalls do LiveScript. http://livescript.net/


Obrigado, mas isso não é uma resposta desde o seu código executa comandos em série de forma assíncrona
disfated

Se você precisar executar uma série de comandos de forma síncrona, como no meu exemplo, ele funcionará. Acho que entendi mal sua pergunta, desculpe. Eu adicionei outra idéia à resposta.
Jason Livesay
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.