nodejs使用child_process模組啟動(exec和spawn)子執行緒任務,子程序例項的kill()方法無效的問題

星小梦發表於2024-08-03

以下內容在win10環境下的執行分析(這裡就不對程序和執行緒做區分了):

  1. child_process.execchild_process.spawn啟動程序的區別。
shell <string> Shell to execute the command with. See Shell requirements and Default Windows shell. Default: '/bin/sh' on Unix, process.env.ComSpec on Windows.
> shell <string> 使用shell執行命令。請參閱外殼要求和預設Windows外殼。預設值:Unix上的“/bin/sh”,process.env。Windows上的ComSpec。
shell <boolean> | <string> If true, runs command inside of a shell. Uses '/bin/sh' on Unix, and process.env.ComSpec on Windows. A different shell can be specified as a string. See Shell requirements and Default Windows shell. Default: false (no shell).
> shell <boolean> | <string> 如果為true,則在shell內執行命令。在Unix上使用'/bin/sh'和process.env。Windows上的ComSpec。可以將其他shell指定為字串。請參閱shell要求和預設Windows shell。預設值:false(無shell)。

child_process.exec啟動的程序是先啟動cmd命令,在把你透過exec傳入的command字串放到cmd中執行,
這樣就會導致有2個程序,1個是程序是cmd命令所啟動的,另一個程序是你傳入的command命令所啟動的。
這就會導致你透過exec獲取到的程序例項其實是cmd程序的PID,而cmd程序所啟動的命令的程序你是獲取不到的,
自然就無法透過kill停止了,因為你停止的只是cmd程序,而不是你傳入執行的command字串命令所在的程序。

正是因此,所以exec適合短時間執行的命令任務,例如dir,tasklist等這種會自己結束的命令。


child_process.spawn命令預設是不在shell中執行,因為shell的預設值是false嗎!這裡我們舉一個spawn命令的例子來說明怎麼使用。

const ls = spawn('C:\\Windows\\System32\\PING.EXE', ['-n 600 127.0.0.1'], {
    shell: false,
    windowsVerbatimArguments : true,
});

ls.stdout.on('data', (data) => {
    debugger
    console.log(`stdout: ${iconv.decode(data, 'gbk')}`);
});

ls.stderr.on('data', (data) => {
    console.error(`stderr: ${data}`);
});

ls.on('close', (code) => {
    console.log(`child process close exited with code ${code}`);
});

ls.on('exit', (code) => {
    console.log(`child process exited with code ${code}`);
});

// 2分鐘後停止子程序。
setTimeout(()=>{
    let kill = ls.kill();
    console.log("模仿主程序停止,停止子程序成功?" + kill);
}, 1000 * 60 * 2)

spawn命令的引數我這裡簡單說明下,詳細的看Nodejs官方文件
spawn命令的第一個引數是要執行的命令的完整路徑(不要攜帶引數,否則會執行報錯,提示找不到命令),

spawn('C:/a/b/c.exe -n 100')這種使用就是錯誤的,正確的使用方式是spawn('C:/a/b/c.exe')這種方式。
而第二個引數是要提供一個String[]字串陣列,但是這個陣列有2種傳值的方式。
第一種方式是['-n 100'],如果你傳入這個引數,當前spawn執行的命令程序會執行後就會瞬間退出,因為命令對這個引數無法解析,這主要是nodejs對命令的引數解析後,
在實際執行的時候引數就不認識了,但是當你配置上windowsVerbatimArguments引數為true值,就可以解決這個解析引數的問題。

windowsVerbatimArguments 在Windows上不進行引數的引用或轉義。在 Unix 上被忽略。當指定 shell 且為 CMD 時,此值自動設定為 true。預設值:false。

完整的例子:

spawn('C:/a/b/c.exe', ['-n 100'], {
windowsVerbatimArguments: true
})

spawn('C:/a/b/c.exe', ['-n 100', '-t'])

其他問題

spawn的stdout輸出的字串亂碼(���� Ping 127.0.0.1 ���� 32 �ֽڵ�����:)怎麼解決?

https://www.npmjs.com/package/iconv-lite ,安裝這個庫,將其轉為GBK編碼進行輸出即可。

ls.stdout.on('data', (data) => {
    console.log(`stdout: ${iconv.decode(data, 'gbk')}`);
});

如果你想說為什麼不使用Buffer.toString進行轉換編碼呢?那是因為Buffer支援的字元編碼有限,不支援GBK字串編碼的轉換。
https://nodejs.org/dist/v18.20.4/docs/api/buffer.html

相關文章