Biết ra lệnh cho máy tính: Kỹ năng shell mà nhiều lập trình viên bỏ quên
Bài viết chia sẻ hành trình của một kỹ sư từ chỗ chỉ biết gõ từng lệnh đơn lẻ trong terminal đến khả năng viết script shell kết nối nhiều chương trình để tự động hóa công việc. Tác giả nhấn mạnh rằng biết shell chỉ có 20% là cú pháp, còn 80% là nắm vững các công cụ dòng lệnh — và việc phụ thuộc hoàn toàn vào GUI có thể khiến lập trình viên bị giới hạn năng lực.
Trong những năm đầu của sự nghiệp, tác giả Will Keleher thú nhận rằng ông không biết cách "ra lệnh" cho chiếc máy tính của mình. Ông có thể chạy vài câu lệnh đơn giản trong terminal — chạy test, cài dependency, khởi động container, ssh vào một máy nào đó — nhưng chỉ dừng lại ở mức gõ từng lệnh một. Terminal khi đó giống như một chiếc GUI tệ nhất thế giới, và ông nghĩ shell chỉ đơn thuần là cách để khởi động những chương trình không có giao diện đồ họa.
Vấn đề cốt lõi là ông không thể yêu cầu máy tính thực hiện bất cứ điều gì liên quan đến logic hoặc kết nối nhiều chương trình lại với nhau. Cụ thể, ông không biết cách:
- Làm việc này rồi làm việc khác
- Nếu một lệnh thất bại thì in ra thông báo lỗi
- Khởi chạy một chương trình ngay khi máy tính bật lên
- Chạy hai lệnh cùng lúc
- Lặp qua tất cả file trong một thư mục và xử lý từng file
- Dùng kết quả của lệnh này làm đầu vào cho lệnh kia
Về lý thuyết, ông hoàn toàn có thể dùng NodeJS để viết những chương trình kiểu này. Nhưng trên thực tế, ông chưa bao giờ làm vậy. Một phần là do tư duy — ông chưa quen nghĩ về những chương trình mình dùng trên dòng lệnh như những thứ có thể điều khiển được. Phần còn lại là do thiếu kỹ năng: ông chưa hiểu đủ rõ các công cụ mình đang dùng để có thể tích hợp chúng vào một script tự viết.
Từ những script chắp vá đến bước ngoặt tư duy
Tác giả kiên trì học shell và dần dần có thể viết được những script có logic cơ bản, chẳng hạn như kiểm tra mã thoát sau khi chạy npm install để phát hiện lỗi và in ra thông báo hướng dẫn. Đoạn script này dùng set +e để cho phép lệnh thất bại mà không làm sập toàn bộ script, sau đó lấy mã thoát $? để kiểm tra, rồi bật lại set -e.
Với người đã quen shell, đoạn code này có thể khiến họ "nổi da gà" vì cách viết vòng vo. Nhưng với tác giả, đó là một bước chuyển mình: lần đầu tiên ông có thể bảo máy tính kết nối các chương trình có sẵn để đạt mục tiêu của mình. Cảm giác ấy giống như khi ông học lập trình lần đầu.
Về sau, khi nhìn lại, tác giả nhận ra đoạn script trên phức tạp một cách không cần thiết. Lệnh
ifthực chất nhận vào các câu lệnh, nên chỉ cần viếtif ! npm install; then ...là đủ — không cầnset +e, không cần bắt$?.
Nghịch lý của những kỹ sư giỏi nhưng ngại shell
Qua nhiều năm, tác giả làm việc cùng rất nhiều kỹ sư tài năng nhưng ít đầu tư thời gian học shell. Họ dựa vào GUI để chạy test, quản lý git, làm việc với cơ sở dữ liệu và xử lý công việc hằng ngày. Cách này ổn — cho đến khi bạn cần làm điều gì đó mà GUI không hỗ trợ.
Tác giả cho rằng rất dễ rơi vào tình trạng bị giới hạn bởi khả năng của GUI. Ông từng chứng kiến những kỹ sư giỏi phải bỏ ra rất nhiều công sức chỉ vì không biết cách dùng while để chạy lặp một lệnh, hay viết vòng for để xử lý mọi file trong một thư mục.
Vấn đề này càng nghiêm trọng khi liên quan đến việc đọc và bảo trì script shell. Hầu hết công ty đều có một lượng logic thiết yếu để build, triển khai, kiểm tra và test code được viết bằng Bash hoặc Zsh. Nếu bạn không thoải mái với ngôn ngữ mà công cụ của mình được viết bằng đó, bạn sẽ khó đọc hoặc cải thiện nó.
Vì sao nhiều script lại được viết bằng ngôn ngữ "lạ"?
Tác giả cho rằng thường thì viết script kết nối các lệnh bằng chính ngôn ngữ được thiết kế để làm việc đó sẽ thuận tiện hơn. Lấy ví dụ đơn giản là chạy test liên tục cho đến khi thất bại:
while pnpm exec mocha ./pathToFile.test.ts; do true; done
So với việc viết bằng NodeJS:
const { execSync } = require("child_process");
while (true) {
try {
execSync(`pnpm exec mocha ./pathToFile.test.ts`, { stdio: "inherit" });
} catch (err) {
console.error("failed", err);
break;
}
}
Phiên bản NodeJS có nhiều "góc cạnh" và cạm bẫy hơn, trong khi shell lại đơn giản hơn. Bạn không cần tạo file, không cần require child_process, không cần cấu hình { stdio: "inherit" } để thấy output.
Tuy nhiên, tác giả cũng lưu ý rằng điều này không có nghĩa bạn buộc phải viết bằng Bash hay Zsh. Với các nhóm quen JavaScript, công cụ như zx có thể giúp việc viết và bảo trì script trở nên dễ chịu hơn. Ruby và Python cũng dễ hơn NodeJS cho việc này.
Tác giả không cho rằng shell luôn dễ hơn cho mọi script build. Khi bài toán đơn giản chỉ là ghép hai chương trình lại, Bash hay Zsh rất tiện. Nhưng ngay khi cần logic và kiểu dữ liệu phức tạp hơn, bạn nên chọn ngôn ngữ giúp biểu diễn và test những thứ đó dễ dàng.
Bí quyết thật sự: 20% cú pháp, 80% hộp đồ nghề
Tác giả nhấn mạnh một điểm quan trọng: nhiều kỹ sư phàn nàn về script build viết bằng shell nhưng chưa thực sự thử chuyển chúng sang ngôn ngữ khác. Ngoài cú pháp, phần lớn việc "học viết script shell" không nằm ở cú pháp. Nếu bạn chuyển một script chỉ ghép các lệnh sang ngôn ngữ khác mà không hiểu rõ hành vi của những lệnh đó, script tạo ra cũng sẽ khó hiểu tương tự.
Biết shell — tức là biết ra lệnh cho máy tính — phụ thuộc rất nhiều vào việc hiểu các chương trình thực hiện công việc bạn muốn. Theo tác giả, biết shell là 20% cú pháp và 80% sở hữu một hộp đồ nghề tốt:
- Biết fzf, bạn có thể tạo tiện ích tìm kiếm mờ tương tác, ví dụ chọn nhánh git nhanh chóng
- Biết tldr hoặc eg, bạn có thể tra ví dụ sử dụng cho bất kỳ lệnh nào khác
- Biết rsync, bạn có thể copy các file đã thay đổi lên một server mạnh hơn để chạy tác vụ nặng
- Biết xargs, bạn có thể xây dựng lệnh dần dần và song song hóa công việc
- Biết sed -i hoặc ast-grep, bạn có thể viết lại các mẫu phức tạp trên nhiều file
- Biết direnv, bạn có thể đảm bảo biến môi trường đúng cho mọi người chạy lệnh trong codebase
- Biết duckdb, bạn có thể viết SQL để truy vấn file CSV và JSON ngay trên máy
- Biết gh, bạn có thể viết script kiểm tra PR và mở PR mới từ CLI
- Biết ngrok, bạn có thể nhanh chóng mở một cổng local để test trên máy khác
Mỗi chương trình bạn học thêm sẽ mở rộng năng lực, vì mỗi công cụ mới đều có thể kết hợp với mọi công cụ bạn đã biết.
Kết luận
Tác giả tự nhận mình còn xa mới là chuyên gia shell, thậm chí kém cỏi so với những người hiểu sâu về nó. Nhưng ông vẫn thu được rất nhiều giá trị từ việc biết đủ cú pháp shell để ghép các chương trình lại, và biết đủ những chương trình mình thực sự muốn ghép.
Với lập trình viên Việt Nam — đặc biệt là những bạn làm backend, DevOps hay đang học về CI/CD — bài học này rất đáng lưu tâm. Trong nhiều công ty công nghệ tại Việt Nam, các pipeline build và deploy thường phụ thuộc vào script shell. Thành thạo dòng lệnh không chỉ giúp bạn đọc hiểu hệ thống hiện có mà còn mở ra khả năng tự động hóa công việc, biến những tác vụ lặp đi lặp lại thành một câu lệnh duy nhất. Nói ngắn gọn: biết ra lệnh cho máy tính là một kỹ năng đáng đầu tư.


