Bash & Shell Scripting · Part 1 — The Shell, the Shebang & Your First Script
Start scripting from zero: what a shell really is, bash vs sh, the shebang line, making a script executable, the three ways to run it, echo vs printf, and exit codes — bilingual, with exercises.
Đây là Phần 1 của series 10 bài đưa bạn từ “copy-paste lệnh” đến viết shell script chắc chắn, mức production. Mỗi phần đều thực hành, có ví dụ thật và bài tập.
Hết series, bạn sẽ đọc, viết, và debug shell script một cách tự tin.
Shell là gì?
Một shell là chương trình đọc lệnh của bạn rồi nhờ hệ điều hành chạy chúng. Khi bạn mở terminal, một shell đang chờ ở dấu nhắc.
Bash là shell phổ biến nhất trên Linux và là mục tiêu scripting mặc định gần như mọi nơi.
Có hai cách bạn dùng shell:
- Tương tác: gõ một lệnh, Enter, xem kết quả.
- Kịch bản: bỏ nhiều lệnh vào một file rồi chạy cả loạt.
Series này nói về cách thứ hai.
bash vs sh — vì sao quan trọng
sh là shell POSIX gốc, tối giản. bash là tập cha với nhiều tính năng hơn hẳn.
Trên nhiều hệ thống /bin/sh không phải bash — có thể là dash. Một script chạy được với bash có thể vỡ dưới sh. Vậy luôn nói rõ bạn nhắm shell nào.
Quy tắc: nếu bạn dùng tính năng bash, hãy khai báo bash ở shebang và chạy bằng bash.
Dòng shebang
Dòng đầu của script cho hệ thống biết trình thông dịch nào sẽ chạy file. Nó bắt đầu bằng #! — gọi là shebang.
#!/usr/bin/env bash
Vì sao dùng /usr/bin/env bash thay vì /bin/bash? Vì env tìm bash trên PATH của người dùng, nên script chạy được dù bash nằm ở /bin, /usr/bin, hay path của Homebrew.
Script đầu tiên của bạn
Tạo file hello.sh:
#!/usr/bin/env bash
# hello.sh — my first script
# Lines starting with # are comments (the shebang is the one exception).
echo "Hello from a script!"
echo "Today is: $(date +%Y-%m-%d)"
Để ý các comment #: mọi thứ sau # trên một dòng đều bị bash bỏ qua. Dùng chúng để giải thích vì sao, không phải cái gì.
Cho phép chạy & chạy script
File .sh mới chỉ là text. Để chạy như một chương trình, bạn cấp quyền execute:
chmod +x hello.sh # add the execute bit
./hello.sh # run it (note the ./)
Cái ./ quan trọng: nghĩa là “file trong thư mục này”. Không có nó, shell chỉ tìm trong PATH và sẽ không thấy file của bạn.
Ba cách chạy script
| Lệnh | Chuyện gì xảy ra |
|---|---|
./hello.sh | Dùng shebang; cần quyền execute |
bash hello.sh | Ép bash; bỏ qua shebang & quyền execute |
source hello.sh (or . hello.sh) | Chạy trong shell hiện tại — biến còn lại sau khi chạy xong |
Bẫy:
./hello.shvàbash hello.shchạy trong shell con, nên biến chúng đặt sẽ biến mất khi xong. Chỉsourcegiữ lại.
echo vs printf
echo in một dòng — nhanh và ổn cho output đơn giản. printf chính xác và portable hơn khi cần định dạng:
echo "Name: Alice"
printf "%-10s %s\n" "Name:" "Alice" # control width
printf "Progress: %d%%\n" 80 # Progress: 80%
Ưu tiên printf cho mọi thứ cần định dạng hoặc nơi flag của echo khác nhau giữa các hệ thống.
Exit code — nhịp tim của scripting
Mỗi lệnh trả về một con số khi kết thúc: 0 là thành công, khác 0 là thất bại. Bạn đọc nó từ biến đặc biệt $?:
ls /tmp
echo "$?" # 0 — it worked
ls /nope 2>/dev/null
echo "$?" # non-zero — it failed
Đây là nền tảng cho if, &&, ||, và xử lý lỗi ở các phần sau. Script của bạn cũng có thể trả về code:
exit 0 # success
exit 1 # generic failure
Lỗi người mới hay mắc
- Quên
chmod +xrồi bối rối vì sao./script.shbáo “Permission denied”. - Quên
./và thấy “command not found”. - Soạn trên Windows và lưu kiểu xuống dòng CRLF — bash nghẹn với
\r. Dùng LF. - Tưởng
/bin/shlà bash.
Bài tập
Thử từng bài trước khi mở lời giải.
- Viết script
whoami-now.shin username và giờ hiện tại, rồi thoát với code0. - Chạy cùng script theo ba cách và giải thích khác biệt với một biến bạn đặt bên trong.
- Cố ý làm một lệnh thất bại rồi in exit code của nó.
Lời giải
#!/usr/bin/env bash
# whoami-now.sh
echo "User: $(whoami)"
printf "Time: %s\n" "$(date +%H:%M:%S)"
exit 0chmod +x whoami-now.sh
./whoami-now.sh # child shell (shebang)
bash whoami-now.sh # child shell (forced bash)
source whoami-now.sh # current shell — any variable set inside survives here
# Exercise 3:
grep something /no/such/file
echo "exit code: $?" # prints a non-zero numbersource chạy các dòng trong shell hiện tại, nên biến gán trong script vẫn còn sau đó. ./ và bash chạy shell con, nên các biến đó biến mất.
Điều cốt lõi
Một script chỉ là các lệnh trong file, được chỉ định interpreter qua shebang, đánh dấu executable, và chạy bằng ./. Theo dõi exit code — nó điều khiển mọi thứ phía sau.