使用 Node.js 執行 Shell 命令
簡介
系統管理員和開發人員經常轉向自動化以減少他們的工作量並改進他們的流程。使用服務器時,自動化任務經常使用 shell 腳本編寫腳本。但是,開發人員可能更喜歡使用更通用的高級語言來完成複雜的任務。許多應用程序還需要與文件系統和其他操作系統級組件進行交互,這通常使用命令行級實用程序更容易完成。
使用 Node.js,我們可以運行 shell 命令並使用 JavaScript 處理它們的輸入和輸出。因此,我們可以用 JavaScript 而不是 shell 腳本語言來編寫大部分這些複雜的操作,從而可能使程序更易於維護。
在本文中,我們將學習使用 child_process
在 Node.js 中執行 shell 命令的各種方法 模塊。
child_proccess 模塊
Node.js 在單個線程中執行其主事件循環。但是,這並不意味著它的所有處理都在那個線程中完成。 Node.js 中的異步任務在其他內部線程中執行。完成後,回調中的代碼或錯誤將返回到主單線程。
這些不同的線程在同一個 Node.js 進程中運行。但是,有時需要創建另一個進程來執行代碼。當一個新進程被創建時,操作系統決定它使用哪個處理器以及如何調度它的任務。
child_process
模塊創建我們的主要 Node.js 進程的新子進程。我們可以用這些子進程執行shell命令。
如果使用得當,使用外部進程可以提高應用程序的性能。例如,如果 Node.js 應用程序的某個功能是 CPU 密集型的,由於 Node.js 是單線程的,它會在運行時阻止其他任務的執行。
但是,我們可以將資源密集型代碼委託給子進程,比如說一個非常高效的 C++ 程序。然後,我們的 Node.js 代碼將在一個新進程中執行該 C++ 程序,而不阻塞它的其他活動,並在完成時處理它的輸出。
我們將用來執行 shell 命令的兩個函數是 exec
和 spawn
.
執行函數
exec()
函數創建一個新的 shell 並執行給定的命令。執行的輸出被緩衝,這意味著保存在內存中,並且可以在回調中使用。
讓我們使用 exec()
函數列出我們當前目錄中的所有文件夾和文件。在名為 lsExec.js
的新 Node.js 文件中 ,編寫如下代碼:
const { exec } = require("child_process");
exec("ls -la", (error, stdout, stderr) => {
if (error) {
console.log(`error: ${error.message}`);
return;
}
if (stderr) {
console.log(`stderr: ${stderr}`);
return;
}
console.log(`stdout: ${stdout}`);
});
首先,我們需要 child_process
我們程序中的模塊,特別是使用 exec()
函數(通過 ES6 解構)。接下來,我們調用 exec()
有兩個參數的函數:
- 帶有我們要執行的 shell 命令的字符串。
- 三個參數的回調函數:
error
,stdout
,stderr
.
我們運行的shell命令是ls -la
,它應該逐行列出我們當前目錄中的所有文件和文件夾,包括隱藏文件/文件夾。回調函數記錄我們是否得到 error
嘗試在 shell 的 stdout
上執行命令或輸出時 或 stderr
流。
如果您運行該 Node.js 文件,您應該會看到類似於以下內容的輸出:
$ node lsExec.js
stdout: total 0
[email protected] 9 arpan arpan 0 Dec 7 00:14 .
[email protected] 4 arpan arpan 0 Dec 7 22:09 ..
[email protected] 1 arpan arpan 0 Dec 7 15:10 lsExec.js
child process exited with code 0
現在我們已經了解瞭如何使用 exec()
運行命令 ,讓我們學習另一種使用 spawn()
執行命令的方法 .
生成函數
spawn()
函數在新進程中執行命令 .此函數使用 Stream API,因此其命令輸出可通過偵聽器獲得。
與之前類似,我們將使用 spawn()
函數列出我們當前目錄中的所有文件夾和文件。讓我們創建一個新的 Node.js 文件,lsSpawn.js
,然後輸入以下內容:
const { spawn } = require("child_process");
const ls = spawn("ls", ["-la"]);
ls.stdout.on("data", data => {
console.log(`stdout: ${data}`);
});
ls.stderr.on("data", data => {
console.log(`stderr: ${data}`);
});
ls.on('error', (error) => {
console.log(`error: ${error.message}`);
});
ls.on("close", code => {
console.log(`child process exited with code ${code}`);
});
我們首先要求 spawn()
child_process
中的函數 模塊。然後,我們創建一個執行 ls
的新進程 命令,傳遞 -la
作為論據。請注意參數是如何保存在數組中而不包含在命令字符串中的。
然後我們設置我們的監聽器。 stdout
ls
的對象 , 觸發 data
命令寫入該流時的事件。同樣,stderr
也會觸發 data
命令寫入該流時的事件。
通過直接在存儲命令引用的對像上偵聽錯誤來捕獲錯誤。如果 child_process
你只會得到一個錯誤 運行命令失敗。
免費電子書:Git Essentials
查看我們的 Git 學習實踐指南,其中包含最佳實踐、行業認可的標準以及隨附的備忘單。停止谷歌搜索 Git 命令並真正學習 它!
close
命令完成時發生事件。
如果我們運行這個 Node.js 文件,我們應該像以前一樣使用 exec()
獲得輸出 :
$ node lsSpawn.js
stdout: total 0
[email protected] 9 arpan arpan 0 Dec 7 00:14 .
[email protected] 4 arpan arpan 0 Dec 7 22:09 ..
[email protected] 1 arpan arpan 0 Dec 7 15:10 lsExec.js
[email protected] 1 arpan arpan 0 Dec 7 15:40 lsSpawn.js
child process exited with code 0
何時使用 exec 和 spawn?
exec()
的主要區別 和 spawn()
是他們返回數據的方式。作為 exec()
將所有輸出存儲在緩衝區中,它比 spawn()
佔用更多內存 ,它會在輸出時流式傳輸。
一般來說,如果您不希望返回大量數據,您可以使用 exec()
為簡單起見。用例的好例子是創建文件夾或獲取文件的狀態。但是,如果您希望命令有大量輸出,那麼您應該使用 spawn()
.一個很好的例子是使用命令來操作二進制數據,然後將其加載到您的 Node.js 程序中。
結論
Node.js 可以使用標準的 child_process
運行 shell 命令 模塊。如果我們使用 exec()
函數,我們的命令將運行,它的輸出將在回調中提供給我們。如果我們使用 spawn()
模塊,它的輸出將通過事件監聽器獲得。
如果我們的應用程序期望我們的命令有很多輸出,我們應該更喜歡 spawn()
超過 exec()
.如果沒有,我們可能會選擇使用 exec()
因為它的簡單性。
既然您可以在 Node.js 外部運行任務,那麼您會構建哪些應用程序?