I/O
Chạy tiến trình con (exec)
child_process.execSync/exec của Node.js so với os/exec (Go), std::process::Command (Rust), Process (Swift) và ProcessBuilder (Java).
- Phiên bản tối thiểu
- Node.js ≥ 14.13.1Go ≥ 1.7Rust ≥ 1.71Swift ≥ 5.7Java ≥ 25
- Đã chạy thử trên
- Node.js 24.12.0Go 1.27.1Rust 1.98.1Swift 6.2.4Java 25.0.4.1
Code Node.js là ES module: lưu file .mjs hoặc đặt "type": "module" trong package.json.
Chạy một lệnh hệ thống có hai kiểu: đợi nó chạy xong rồi mới tiếp tục (sync), hoặc chạy mà không chặn rồi xử lý kết quả sau (async). Go không có sự phân biệt đó: cmd.Run() luôn chặn (block) goroutine gọi nó. Rust std cũng vậy — Command::output() chặn thread hiện tại; muốn "async" thật sự phải dùng tokio::process::Command (không có trong std). Swift's Process cũng chỉ có API đồng bộ (waitUntilExit()); "async có timeout" phải tự ghép bằng Task. Java có sẵn cả hai: Process.waitFor() (block) và Process.onExit() trả về CompletableFuture<Process>.
Chạy đồng bộ (sync)
import { execSync } from 'node:child_process'
const output = execSync(`echo 'hello world'`)
console.log(output.toString())package main
import (
"fmt"
"os/exec"
)
func main() {
output, err := exec.Command("echo", "hello world").Output()
if err != nil {
panic(err)
}
fmt.Println(string(output))
}use std::process::Command;
fn main() {
let output = Command::new("echo")
.arg("hello world")
.output() // block cho tới khi tiến trình con thoát
.unwrap();
print!("{}", String::from_utf8_lossy(&output.stdout));
}import Foundation
let process = Process()
process.executableURL = URL(fileURLWithPath: "/bin/echo")
process.arguments = ["hello world"]
let pipe = Pipe()
process.standardOutput = pipe
try process.run()
process.waitUntilExit() // block cho tới khi tiến trình con thoát
let data = pipe.fileHandleForReading.readDataToEndOfFile()
print(String(data: data, encoding: .utf8) ?? "", terminator: "")void main() throws Exception {
Process process = new ProcessBuilder("echo", "hello world").start();
String output = new String(process.getInputStream().readAllBytes());
process.waitFor(); // block cho tới khi tiến trình con thoát
IO.print(output);
}hello worldChạy bất đồng bộ (async) có timeout
import { exec } from 'node:child_process'
import { promisify } from 'node:util'
const execAsync = promisify(exec)
const { stdout, stderr } = await execAsync(`echo 'hello world'`, { timeout: 5000 })
if (stderr) {
console.error(stderr)
}
console.log(stdout)package main
import (
"context"
"os"
"os/exec"
"time"
)
func main() {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
cmd := exec.CommandContext(ctx, "echo", "hello world")
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil {
panic(err)
}
}// Cargo.toml: tokio = { version = "1", features = ["full"] }
use std::time::Duration;
use tokio::process::Command;
use tokio::time::timeout;
#[tokio::main]
async fn main() {
let child = Command::new("echo")
.arg("hello world")
.stdout(std::process::Stdio::piped()) // không piped thì stdout kế thừa từ cha, wait_with_output() sẽ trả về rỗng
.kill_on_drop(true) // hết timeout thì drop future sẽ kill tiến trình con
.spawn()
.unwrap();
let output = timeout(Duration::from_secs(5), child.wait_with_output())
.await
.expect("timed out")
.unwrap();
print!("{}", String::from_utf8_lossy(&output.stdout));
}import Foundation
let process = Process()
process.executableURL = URL(fileURLWithPath: "/bin/echo")
process.arguments = ["hello world"]
let pipe = Pipe()
process.standardOutput = pipe
try process.run()
let timeoutTask = Task {
try await Task.sleep(for: .seconds(5))
if process.isRunning { process.terminate() } // hết timeout thì kill tiến trình con
}
process.waitUntilExit() // vẫn block: Process không có API async để chờ thoát
timeoutTask.cancel()
let data = pipe.fileHandleForReading.readDataToEndOfFile()
print(String(data: data, encoding: .utf8) ?? "", terminator: "")import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
void main() throws Exception {
Process process = new ProcessBuilder("echo", "hello world")
.redirectErrorStream(true)
.start();
// đọc stdout song song ngay từ đầu — nếu đợi tiến trình thoát rồi mới đọc,
// tiến trình con có thể bị treo khi buffer output đầy
CompletableFuture<String> output = CompletableFuture.supplyAsync(() -> {
try {
return new String(process.getInputStream().readAllBytes());
} catch (Exception e) {
throw new RuntimeException(e);
}
});
try {
process.onExit() // CompletableFuture<Process>, không block thread gọi cho tới .join()
.orTimeout(5, TimeUnit.SECONDS)
.join();
} catch (Exception timedOut) {
process.destroyForcibly(); // hết timeout thì kill tiến trình con
}
IO.print(output.join());
}hello worldTham khảo: github.com/miguelmota/golang-for-nodejs-developers#exec-sync