node.js รันคำสั่งระบบพร้อมกัน


171

ฉันต้องการฟังก์ชั่นnode.js

result = execSync('node -v');

ที่จะพร้อมดำเนินการบรรทัดคำสั่งที่กำหนดและกลับ stdout'ed ทั้งหมดของข้อความคำสั่งที่

PS การซิงค์ผิด ฉันรู้ว่า. เพียงเพื่อการใช้งานส่วนตัว

UPDATE

ตอนนี้เรามีวิธีการแก้ปัญหาของ mgutz ซึ่งให้รหัสออก แต่ไม่ใช่ stdout! ยังคงรอคำตอบที่แม่นยำยิ่งขึ้น

UPDATE

mgutzอัปเดตคำตอบของเขาและวิธีแก้ปัญหาอยู่ที่นี่ :)
นอกจากนี้ตามที่dgo.aกล่าวว่ามีโมดูลแบบสแตนด์อโลนexec-sync

อัพเดท 2014-07-30

ShellJS lib มาถึงแล้ว พิจารณาว่านี่เป็นตัวเลือกที่ดีที่สุดในตอนนี้


อัพเดท 2015-02-10

ในที่สุด! NodeJS 0.12 รองรับexecSyncโดยกำเนิด
ดูเอกสารอย่างเป็นทางการ


26
จะไม่ปล่อยให้ตัวเองได้รับหลงกลซิงค์ไม่ผิด ... แม้ใน NodeJS ทั้งหมดของรหัสของคุณจะถูกดำเนินการพร้อมกันเว้นแต่ว่าคุณได้เรียกวิธี async พ ... ถ้าทุกอย่างถูกทำวิธีที่ไม่ตรงกันไม่มีอะไรจะเคยทำได้ การเลือกวิธีอะซิงโครนัสไม่ได้หมายความว่าการคำนวณแบบยาวของคุณจะไม่บล็อกเซิร์ฟเวอร์ มันเป็นทางเลือก ที่ผู้ผลิตของ Node เลือกที่จะให้วิธีการระบบไฟล์แบบซิงโครนัสพร้อมกับ async นั้นเพิ่งจะแสดงให้เห็นว่ามีที่สำหรับพวกเขาเช่นกัน
ไหล

2
เราจะหา "ไลบรารี่การจำลองเชลล์ Unix" ได้จากที่ไหน?
Florian

คำตอบ:


153

Node.js (ตั้งแต่รุ่น 0.12 เป็นต้นไป) execSync:

child_process.execSync(command[, options])

ตอนนี้คุณสามารถทำสิ่งนี้โดยตรง:

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

และมันจะทำสิ่งที่คุณคาดหวัง (ค่าดีฟอลต์คือไพพ์ผลลัพธ์ i / o ไปยังกระบวนการพาเรนต์) โปรดทราบว่าคุณสามารถทำได้ในspawnSyncตอนนี้


6
หลังจาก 10 ชั่วโมงแห่งความสิ้นหวัง ขอบคุณ DUDE
Tom Dev

ฉันจะตัดการเชื่อมต่อจากกระบวนการย่อยนี้ได้อย่างไร
JulianSoto


54

ดูไลบรารีexecSync

มันค่อนข้างง่ายที่จะทำอย่างไรกับโหนด FFI ฉันจะไม่แนะนำสำหรับกระบวนการเซิร์ฟเวอร์ แต่สำหรับยูทิลิตี้การพัฒนาทั่วไปมันก็เสร็จสิ้นแล้ว ติดตั้งห้องสมุด

npm install node-ffi

สคริปต์ตัวอย่าง:

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

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

[แก้ไข มิ.ย. 2555: วิธีรับ 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
คุณจะทำอย่างไรเพื่อให้ได้รับทุกสิ่งที่ส่งมาstdoutจากที่นี่ ทั้งหมดที่ฉันจะได้รับคือรหัสทางออกกระบวนการ
Mark Kahn

@cwolves: ฉันคิดว่า async น่าจะดีกว่านี้ ( คำตอบของ Ivo )
pvorb

@pvorb - ใช่ยกเว้นเมื่อคุณไม่สามารถใช้ async :)
Mark Kahn

1
มีเหตุผลที่ถูกต้องในการไม่ใช้ค้อน async สำหรับเล็บทุกอัน ตัวอย่างเช่นเครื่องมือเทมเพลตนั้นเป็นแบบอะซิงโครนัสใน Express 3 และฟังก์ชั่นตัวช่วย (ท้องถิ่น) จะต้องมีการซิงโครนัส ถ้าฟังก์ชันตัวช่วยเหล่านั้นจำเป็นต้องรวบรวมไฟล์ Less แบบอะซิงโครนัสทันที?
mgutz

8
ผมสงสัยว่าทำไมแบบนี้ไม่ได้เป็นส่วนหนึ่งของexecSync child_processฉันคิดว่ามันควรจะเป็น
Michael Härtl

31

ใช้โมดูลShellJS

ฟังก์ชั่นexecโดยไม่ต้องให้การโทรกลับ

ตัวอย่าง:

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

2
โปรดทราบว่าในขณะที่เขียนเอกสารเอกสารระบุว่าซิงโครนัสexec()เป็นซีพียูเข้มข้นสำหรับกระบวนการที่ยาวนาน
Aram Kocharyan

1
ตัวเลือก {silent: true} เป็นกุญแจสำคัญ
Nick

1
สิ่งนี้จะทำอะไร (นอกเหนือจากการให้ไวยากรณ์ที่สั้นลง) สิ่งนี้ไม่ได้? const execSync = require('child_process').execSync; code = execSync('node -v');
user2503764

23

มีโมดูลที่ยอดเยี่ยมสำหรับการควบคุมการไหลใน Node.js เรียกว่าเป็นasyncblock หากการตัดโค้ดในฟังก์ชั่นนั้นใช้ได้สำหรับกรณีของคุณตัวอย่างต่อไปนี้อาจได้รับการพิจารณา:

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
เขาถามอย่างชัดเจนเกี่ยวกับรุ่นซิงค์ไม่ใช่ควบคุมโฟลว์ไลบรารี
Alexey Petrushin

22
@AlexeyPetrushin ทุกคำถามที่นี่เป็นเรื่องเกี่ยวกับเป้าหมายไม่ใช่วิธีการเฉพาะเพื่อให้บรรลุ ขอบคุณสำหรับ downvoting แม้ว่า
คว้า

1
นี่เป็นคำตอบที่มีประโยชน์มากสำหรับผู้ใช้ Windows; การติดตั้งexec-syncหรือffiบน Windows มีค่าใช้จ่ายจำนวนมาก (VC ++, SDK, Python และอื่น ๆ ) แต่นี่จะเบากว่า
Mendhak

10

สิ่งนี้เป็นไปไม่ได้ใน Node.js ทั้งสองchild_process.spawnและchild_process.execถูกสร้างขึ้นจากพื้นดินเป็น async

ดูรายละเอียดได้ที่: https://github.com/ry/node/blob/master/lib/child_process.js

หากคุณต้องการให้มีการบล็อกนี้ให้ใส่ทุกอย่างที่ต้องเกิดขึ้นหลังจากนั้นในการติดต่อกลับหรือสร้างคิวของคุณเองเพื่อจัดการสิ่งนี้ในแบบบล็อกฉันคิดว่าคุณสามารถใช้Async.jsสำหรับงานนี้

หรือในกรณีที่คุณมีเวลามากเกินไปให้แฮ็คใน Node.js ด้วยตนเอง


12
แปลกเพราะโมดูลระบบไฟล์มีการโทรแบบซิงโครนัส ทำไมไม่ดำเนินการด้วย?
อัลเฟรด

4
@Alfred การเรียกซิงค์ Sync ส่วนใหญ่จะอยู่ในนั้นเพื่อโหลดการกำหนดค่าเมื่อเริ่มต้นโปรแกรม
Ivo Wetzel

3
@IvoWetzel - tisk tisk ... เราไม่ได้เรียนรู้ที่จะไม่พูดอะไรบางอย่างที่เป็นไปไม่ได้ใช่ไหม ;) ดูโซลูชันของฉันด้านล่าง
Marcus Pope

1
@IvoWetzel "การซิงค์ FS โทร ... " - ถูกต้องและบางครั้งคุณต้องการพูดออกคำสั่งเพื่อคอมไพล์บางอย่างเมื่อเริ่มต้นโปรแกรมและดำเนินการต่อให้เสร็จสมบูรณ์ - เนื่องจากมีการเรียกซิงค์ FS โดยไม่ต้องทำการซิงค์ ดูเหมือนการกำกับดูแล ฉันทั้งหมดเป็นแบบอะซิงโครนัส แต่แบบซิงโครนัสมีข้อดีและการใช้เคส ต้องใช้มันอย่างรอบคอบ
ไหล

Async เป็นเรื่องปกติ แต่ถ้า WidgetB ขึ้นอยู่กับผลลัพธ์สุดท้ายของ WidgetA async ทั้งหมดในโลกจะไม่ทำงานให้เสร็จ บางครั้งกระบวนการต้องมีการซิงโครนัส ลองทำอาหารแบบอะซิงโครนัส ;)
Lloyd Sargent

9

นี่เป็นวิธีที่ง่ายที่สุดที่ฉันพบ:

exec-Sync : https://github.com/jeremyfa/node-exec-sync
(เพื่อไม่ให้สับสนกับ execSync) เรียกใช้
คำสั่งเชลล์แบบซิงโครนัส ใช้สิ่งนี้สำหรับสคริปต์การโอนย้ายโปรแกรม cli แต่ไม่ใช่สำหรับรหัสเซิร์ฟเวอร์ปกติ

ตัวอย่าง:

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

7

เพียงแค่เพิ่มว่าแม้ว่าจะมีเพียงไม่กี่ usecases ที่คุณควรใช้พวกเขาspawnSync/ execFileSync/ execSyncถูกเพิ่มไปยัง node.js ในการกระทำเหล่านี้: https://github.com/joyent/node/compare/d58c206862dc...e8df2676748e


นี่หมายความว่าเราจะมีมันใน v0.12 หรือไม่?
ปิดตัว

@disfated ใช่: strongloop.com/strongblog/…
balupton


3

ฉันคุ้นเคยกับการใช้"synchronous"สิ่งต่าง ๆ ในตอนท้ายของฟังก์ชั่นโทรกลับ ไม่ค่อยดี แต่ใช้งานได้ หากคุณจำเป็นต้องใช้ลำดับของการดำเนินการบรรทัดคำสั่งคุณจำเป็นต้องตัดexecเข้าไปในฟังก์ชั่นที่มีชื่อและเรียกมันซ้ำ รูปแบบนี้ดูเหมือนจะใช้งานได้สำหรับฉัน:

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

ฉันมีปัญหาที่คล้ายกันและฉันสิ้นสุดการเขียนโหนดส่วนขยายสำหรับสิ่งนี้ คุณสามารถตรวจสอบที่เก็บ git มันเป็นโอเพนซอร์สและฟรีและทุกสิ่งที่ดี!

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

ExecXI เป็นส่วนขยายโหนดที่เขียนใน C ++ เพื่อรันคำสั่งเชลล์ทีละคำสั่งเอาท์พุทคำสั่งไปยังคอนโซลแบบเรียลไทม์ ทางเลือกที่ถูกผูกมัดและไม่ จำกัด มีอยู่; หมายความว่าคุณสามารถเลือกที่จะหยุดสคริปต์หลังจากคำสั่งล้มเหลว (ถูกล่ามโซ่) หรือคุณสามารถดำเนินการต่อราวกับว่าไม่มีอะไรเกิดขึ้น!

คำแนะนำการใช้งานอยู่ในไฟล์ ReadMe รู้สึกฟรีเพื่อดึงคำขอหรือส่งปัญหา!

แก้ไข: อย่างไรก็ตามมันยังไม่ส่งคืน stdout เลย ... เพียงแค่แสดงผลแบบเรียลไทม์ มันทำตอนนี้ ฉันเพิ่งปล่อยมันวันนี้ บางทีเราสามารถสร้างมันขึ้นมาได้

อย่างไรก็ตามฉันคิดว่ามันคุ้มค่าที่จะพูดถึงมัน


ตรงนี้เป็นสิ่งที่ฉันกำลังมองหา ขอบคุณที่สละเวลาทำสิ่งนี้
thealfreds

สิ่งเดียวที่ฉันไม่ได้คิดและนำมาใช้คือการกำหนดค่าการสร้างสำหรับรุ่น nodejs ที่แตกต่างกัน ฉันเดาว่าฉันเขียนไว้ในโหนด 0.8 ( travis-ci.org/aponxi/npm-execxi/builds/5248535 ) ดังนั้นตราบใดที่npm installสำเร็จ (กล่าวอีกนัยหนึ่งว่าคอมไพล์ปลั๊กอิน) จากนั้นก็เป็นการดีสำหรับการผลิต นอกเหนือจากการกำหนดเป้าหมายรุ่น nodejs ที่แตกต่างกันแล้วฉันจะบอกว่ามันได้รับการปรับปรุงเพื่อการผลิต หากมีข้อบกพร่องใด ๆ คุณสามารถส่งในคำขอดึงหรือโพสต์ปัญหาที่ GitHub :)
โลแกน

1

คุณสามารถทำการเชลล์แบบซิงโครนัสใน nodejs ได้

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\\"));

แก้ไข - ตัวอย่างนี้มีไว้สำหรับสภาพแวดล้อม windows ปรับสำหรับความต้องการของคุณเอง linux หากจำเป็น


ใช่และเร็วที่สุดเท่าที่ซีพียูของคุณจะสามารถเรียก ... แต่เฮ้เมื่อคุณ "ต้องการ" ทำอะไรชั่วร้ายซาตานเป็นคนของคุณใช่มั้ย
Marcus Pope

ตกลงนี่คือความชั่วร้ายที่บริสุทธิ์ แต่น่ากลัว ฉันต้องการสิ่งนี้เพื่อจัดการกับเหตุการณ์ filesystem browserify.on ('register') ที่ไม่มีการติดต่อกลับ บันทึกวันของฉัน!
Robert Gould

คุณช่วยอธิบายได้ไหมว่าทำไมมันถึงใช้ได้กับเอ็นจิ้นจาวาสคริปต์ ไม่ควรที่จะดำเนินการในขณะที่วงในขีดเดียวกันในขณะที่ผู้บริหารดำเนินการในหนึ่งต่อไป?
badunk

การใช้ CPU สูงสุดโดยการรอไม่ว่างเป็นการออกแบบที่ไม่ดี
Louis

@ Louis-DominiqueDubeau แน่นอน แต่ไม่มีทางเลือกอื่นใดที่ไม่ได้ขึ้นอยู่กับแหล่งข้อมูลบุคคลที่สามที่อาจจะใช่หรือไม่ใช่แพลตฟอร์มข้าม มันไม่ได้เป็น maxing out ที่แท้จริงของ CPU เพราะระบบปฏิบัติการจะไม่ให้ความสำคัญกับกระบวนการ nodejs อย่างเต็มที่ ฉันคิดว่าการใช้งานการซิงค์ของ shell ops อยู่บนขอบฟ้าหรืออาจจะอยู่ที่นี่แล้ว
Marcus Pope

1

จริง ๆ แล้วฉันมีสถานการณ์ที่ฉันต้องการเรียกใช้คำสั่งหลายคำสั่งทีละคำสั่งจาก package.json ติดตั้งสคริปต์ล่วงหน้าเพื่อให้ทำงานได้ทั้งบน Windows และ Linux / OSX ดังนั้นฉันจึงไม่สามารถพึ่งพาโมดูลที่ไม่ใช่คอร์ได้

ดังนั้นนี่คือสิ่งที่ฉันมาด้วย:

#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()

คุณสามารถใช้สิ่งนี้:

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

แก้ไข: ตามที่กล่าวมาสิ่งนี้จะไม่ส่งคืนผลลัพธ์จริงหรืออนุญาตให้คุณใช้ผลลัพธ์ของคำสั่งในโปรแกรม Node อีกแนวคิดหนึ่งสำหรับการใช้ LiveScript backcalls http://livescript.net/


ขอบคุณ แต่นี่ไม่ใช่คำตอบเนื่องจากโค้ดของคุณจะรันคำสั่งในซีรีย์แบบอะซิงโครนัส
ทำให้หมดกำลังใจ

หากคุณต้องการรันชุดคำสั่งแบบซิงโครนัสตามตัวอย่างของฉันมันจะทำงาน ฉันคิดว่าฉันเข้าใจคำถามของคุณผิดขออภัย ฉันได้เพิ่มแนวคิดอื่นให้กับคำตอบ
Jason Livesay
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.