
Linux command line - Nền tảng
2 tháng 9, 2026
Mục lục
- Shell là gì
- Di chuyển trong hệ thống
- Khám phá hệ thống
- Thao tác file và thư mục
- Làm việc với lệnh
- Redirection
- Expansion — cách shell nhìn thấy lệnh
- Mẹo bàn phím
- Permission
- Process
Chương 1 — Shell là gì
Khái niệm
Shell là chương trình nhận lệnh bạn gõ, dịch ra và bảo hệ điều hành thi hành. Nó là lớp trung gian giữa bạn và nhân hệ điều hành (kernel).
Phân biệt hai thứ hay bị gọi lẫn:
| Là gì | |
|---|---|
| Terminal | Cửa sổ ứng dụng (Terminal.app, iTerm2, Warp) — chỉ là cái khung hiển thị |
| Shell | Chương trình chạy bên trong khung đó, thực sự xử lý lệnh (zsh, bash, fish) |
macOS từ Catalina dùng zsh mặc định. Trước đó là bash. Đa số lệnh giống nhau, khác chủ yếu ở phần cấu hình và tự động hoàn thành.
echo $SHELL # shell mặc định của bạn
echo $0 # shell đang chạy hiện tại
zsh --version
Prompt
Dòng chờ lệnh có dạng:
bizzidev@tiennts-MacBook-Air ~ %
│ │ │ │
│ │ │ └── ký hiệu prompt (% cho zsh, $ cho bash)
│ │ └──── thư mục hiện tại (~ = home)
│ └──────────────────────── tên máy
└────────────────────────────────── tên user
Nếu ký hiệu là # thay vì % hoặc $, bạn đang chạy dưới quyền root — cẩn thận.
Lệnh đầu tiên
date # ngày giờ
cal # lịch tháng này
whoami # bạn là ai
hostname # tên máy
uname -a # thông tin hệ điều hành
uptime # máy chạy được bao lâu
df -h # dung lượng ổ đĩa
free # (Linux) RAM — macOS dùng vm_stat
Lịch sử lệnh
Shell nhớ lệnh bạn đã gõ:
history # xem toàn bộ
history | tail -20 # 20 lệnh gần nhất
!! # chạy lại lệnh vừa rồi
!123 # chạy lại lệnh số 123 trong history
Mẹo dùng hằng ngày: gõ lệnh xong mới nhận ra cần quyền root → gõ sudo !! là chạy lại y hệt với sudo.
Lịch sử lưu trong file ~/.zsh_history. Nó là file text bình thường, grep được.
Copy/paste trong terminal
Cmd+C / Cmd+V hoạt động bình thường trên macOS. Nhưng lưu ý Ctrl+C không phải copy — nó gửi tín hiệu dừng chương trình đang chạy.
Chương 2 — Di chuyển trong hệ thống
Cây thư mục một gốc
Unix không có ổ C:, D:. Mọi thứ nằm dưới một gốc duy nhất /. Ổ cứng ngoài, USB đều được gắn (mount) vào một thư mục nào đó dưới gốc này.
/
├── Applications ← app dùng chung mọi user
├── Users
│ ├── bizzidev ← home của bạn
│ └── admin
├── System ← ruột macOS, đừng đụng
├── Library ← thư viện dùng chung
├── usr
│ └── local/bin ← binary cài thêm
├── opt/homebrew ← Homebrew (Apple Silicon)
├── etc ← file cấu hình
├── var/log ← log hệ thống
└── tmp ← file tạm
Ba lệnh nền tảng
pwd # print working directory — đang đứng ở đâu
cd <đường_dẫn> # change directory — chuyển thư mục
ls # list — liệt kê nội dung
Đường dẫn tuyệt đối vs tương đối
cd /Users/bizzidev/projects # tuyệt đối: bắt đầu bằng /, luôn đúng dù đứng đâu
cd projects # tương đối: tính từ chỗ hiện tại
Ký hiệu đặc biệt:
| Ký hiệu | Nghĩa |
|---|---|
. | Thư mục hiện tại |
.. | Thư mục cha |
~ | Home của bạn (/Users/bizzidev) |
~user | Home của user khác |
- | Thư mục vừa rời khỏi |
cd .. # lên một cấp
cd ../.. # lên hai cấp
cd ~ # về home
cd # cũng về home
cd - # quay lại chỗ vừa rời (rất tiện khi nhảy qua lại 2 thư mục)
Vì sao cần ./ khi chạy script
./deploy.sh # chạy script trong thư mục hiện tại
Không có ./ shell sẽ đi tìm deploy.sh trong $PATH (danh sách thư mục chứa lệnh), không tìm trong thư mục hiện tại. Đây là biện pháp bảo mật: tránh việc bạn cd vào một thư mục lạ, gõ ls, và vô tình chạy file ls độc hại người ta đặt sẵn ở đó.
Bài tập
pwd
cd /Applications && pwd
cd ~ && pwd
cd - && pwd # quay lại /Applications
cd ~/Downloads && ls
Chương 3 — Khám phá hệ thống
ls và các flag
ls # cơ bản
ls -l # long: chi tiết từng dòng
ls -a # all: gồm file ẩn (bắt đầu bằng dấu chấm)
ls -h # human: size dạng 4.2K thay vì 4300
ls -t # sắp theo thời gian sửa, mới nhất trước
ls -r # đảo ngược thứ tự
ls -S # sắp theo kích thước
ls -R # đệ quy vào thư mục con
ls -d <dir> # thông tin của CHÍNH thư mục, không phải nội dung
Kết hợp thường dùng:
ls -lah # chi tiết + file ẩn + size dễ đọc
ls -lt # xem file nào vừa sửa gần nhất
ls -ld ~ # xem quyền của chính thư mục home
ls -ld là chỗ hay nhầm: thiếu -d thì ls liệt kê nội dung bên trong thay vì thông tin của thư mục đó.
Đọc output của ls -l
-rw-r--r-- 1 bizzidev staff 1024 Sep 2 20:15 package.json
│└┬┘└┬┘└┬┘ │ │ │ │ │ │
│ │ │ │ │ │ │ │ │ └── tên
│ │ │ │ │ │ │ │ └─────────────── lần sửa cuối
│ │ │ │ │ │ │ └──────────────────────── size (byte)
│ │ │ │ │ │ └──────────────────────────────── group sở hữu
│ │ │ │ │ └──────────────────────────────────────── user sở hữu
│ │ │ │ └─────────────────────────────────────────────── số hard link
│ │ │ └──────────────────────────────────────────────────── quyền others
│ │ └─────────────────────────────────────────────────────── quyền group
│ └────────────────────────────────────────────────────────── quyền owner
└──────────────────────────────────────────────────────────── loại file
Ký tự đầu tiên cho biết loại:
| Ký tự | Loại |
|---|---|
- | File thường |
d | Directory |
l | Symbolic link (lối tắt) |
c / b | Thiết bị (character/block device) |
Xem nội dung file
cat file.txt # in toàn bộ ra màn hình
cat -n file.txt # kèm số dòng
less file.txt # xem từng trang (q thoát, / tìm, G cuối, g đầu)
head file.txt # 10 dòng đầu
head -50 file.txt # 50 dòng đầu
tail file.txt # 10 dòng cuối
tail -f app.log # theo dõi realtime — dùng nhiều nhất khi debug server
less là công cụ đọc chuẩn. Phím trong less:
| Phím | Tác dụng |
|---|---|
Space / b | Trang sau / trang trước |
g / G | Đầu file / cuối file |
/text | Tìm xuôi, n để tới kết quả kế |
?text | Tìm ngược |
q | Thoát |
Xác định loại file
file <tên_file> # đoán loại nội dung thật sự
Hữu ích vì Unix không dựa vào phần đuôi mở rộng. File tên data.txt vẫn có thể là ảnh PNG.
Tìm file
find . -name "*.log" # tìm theo tên, từ thư mục hiện tại
find . -name "*.js" -not -path "*/node_modules/*"
find . -type d -name "dist" # chỉ tìm thư mục
find . -size +100M # file lớn hơn 100MB
find . -mtime -7 # sửa trong 7 ngày qua
mdfind -name "Docker.app" # macOS: tra Spotlight index, nhanh hơn nhiều
find duyệt thật từng thư mục nên chậm nhưng chính xác. mdfind tra index có sẵn nên nhanh nhưng phụ thuộc Spotlight.
Dung lượng
du -sh * # dung lượng từng thư mục con
du -sh * | sort -hr # sắp giảm dần — tìm thủ phạm chiếm ổ đĩa
df -h # dung lượng còn trống của các ổ
Mẹo cho dev Node: chạy du -sh */node_modules 2>/dev/null | sort -hr để xem project nào đang ngốn ổ đĩa nhất.
Chương 4 — Thao tác file và thư mục
Tạo
mkdir mydir # tạo thư mục
mkdir -p a/b/c # tạo cả cây, không báo lỗi nếu đã tồn tại
touch file.txt # tạo file rỗng (hoặc cập nhật timestamp)
mkdir -p cực hay dùng trong script vì nó không văng lỗi khi thư mục đã có sẵn.
Copy
cp file.txt backup.txt # copy file
cp file.txt ~/Documents/ # copy vào thư mục
cp -r src/ dest/ # copy đệ quy (bắt buộc với thư mục)
cp -i file.txt dest.txt # hỏi trước khi ghi đè
cp -v file.txt dest.txt # verbose: in ra đang làm gì
cp -a src/ dest/ # archive: giữ nguyên quyền, timestamp, symlink
Di chuyển và đổi tên
mv old.txt new.txt # đổi tên
mv file.txt ~/Documents/ # di chuyển
mv -i file.txt dest.txt # hỏi trước khi ghi đè
Unix không có lệnh "rename" riêng — đổi tên chính là di chuyển sang tên mới.
Xóa
rm file.txt # xóa file
rm -r mydir/ # xóa thư mục và toàn bộ bên trong
rm -f file.txt # force: không hỏi, không báo lỗi nếu không tồn tại
rm -i file.txt # hỏi xác nhận từng file
Cảnh báo: rm không có thùng rác. Xóa là mất vĩnh viễn.
Thói quen an toàn:
- Chạy
ls <pattern>trước để xem chính xác sẽ xóa gì, rồi mới đổilsthànhrm - Không bao giờ gõ
rm -rfvới đường dẫn có biến chưa kiểm tra:rm -rf $DIR/— nếu$DIRrỗng, lệnh thànhrm -rf / - Cài
trash(brew install trash) để xóa vào thùng rác thay vì xóa thẳng
Wildcard
Ký tự đại diện, shell tự khớp tên file:
| Mẫu | Khớp với |
|---|---|
* | Bất kỳ chuỗi nào (kể cả rỗng) |
? | Đúng một ký tự |
[abc] | Một trong các ký tự a, b, c |
[0-9] | Một chữ số |
[!abc] | Bất kỳ ký tự nào trừ a, b, c |
{jpg,png} | jpg hoặc png |
ls *.js # tất cả file .js
ls test?.js # test1.js, test2.js — không khớp test10.js
ls *.{js,ts} # cả js và ts
rm -i *.log # xóa log, có hỏi
Symbolic link
Link là file "lối tắt" trỏ tới file khác:
ln -s /path/to/real/file linkname # tạo symlink
ls -l linkname # thấy mũi tên -> chỉ đích
readlink linkname # in ra đích
Đây chính là cơ chế của docker, node, code trong /usr/local/bin — chúng chỉ là symlink trỏ vào app thật.
ls -l $(which node) # xem node thực sự nằm đâu
Chương 5 — Làm việc với lệnh
Bốn loại "lệnh"
Khi bạn gõ một từ, nó có thể là một trong bốn thứ:
| Loại | Ví dụ | Đặc điểm |
|---|---|---|
| Executable | ls, node | File chương trình thật trên đĩa |
| Builtin | cd, echo | Nằm sẵn trong shell, không có file riêng |
| Function | Hàm shell tự định nghĩa | Trong .zshrc |
| Alias | ll = ls -la | Tên rút gọn tự đặt |
Phân biệt bằng:
type ls # cho biết ls là loại gì
which node # đường dẫn file thật
command -v docker # cách chuẩn để kiểm tra lệnh có tồn tại không (dùng trong script)
PATH — vì sao gõ node là chạy được
PATH là danh sách thư mục shell sẽ lần lượt tìm khi bạn gõ một lệnh:
echo $PATH
# /opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:...
Các thư mục ngăn cách bởi dấu :, tìm theo thứ tự từ trái sang, gặp cái đầu tiên là dùng luôn.
Đây là lý do lỗi command not found: docker — file docker không nằm trong bất kỳ thư mục nào của PATH.
which -a node # liệt kê TẤT CẢ bản node trong PATH, thấy được cái nào đang thắng
Thêm đường dẫn vào PATH, viết trong ~/.zshrc:
export PATH="$HOME/.local/bin:$PATH"
Đặt trước $PATH nghĩa là ưu tiên cao hơn, đặt sau thì ưu tiên thấp hơn.
Alias
Tự đặt tên rút gọn, viết trong ~/.zshrc:
alias ll='ls -lah'
alias gs='git status'
alias gp='git pull'
alias ..='cd ..'
alias ports='lsof -i -P | grep LISTEN'
alias nrd='npm run dev'
Áp dụng ngay không cần mở terminal mới:
source ~/.zshrc
Tra tài liệu
man ls # tài liệu đầy đủ (q thoát, / tìm)
ls --help # bản ngắn (một số lệnh macOS không có)
tldr ls # bản chỉ có ví dụ thực dụng (brew install tldr)
Cấu trúc một trang man: NAME, SYNOPSIS (cú pháp), DESCRIPTION (từng flag), EXAMPLES.
Trong SYNOPSIS: [...] là tùy chọn, ... là lặp được nhiều lần.
Chạy nhiều lệnh
cmd1 ; cmd2 # chạy tuần tự, không quan tâm kết quả
cmd1 && cmd2 # chỉ chạy cmd2 nếu cmd1 THÀNH CÔNG
cmd1 || cmd2 # chỉ chạy cmd2 nếu cmd1 THẤT BẠI
Ví dụ thực tế:
npm run build && npm run deploy # chỉ deploy khi build ok
cd ~/project && npm install && npm start
mkdir -p logs || echo "khong tao duoc"
Toán tử && dựa trên exit code: 0 = thành công, khác 0 = lỗi.
ls /khong-ton-tai
echo $? # in exit code của lệnh vừa rồi
Nhớ $? — nó là cách bạn kiểm tra lệnh trước có chạy ổn không, đặc biệt trong CI script.
Chương 6 — Redirection
Ba luồng chuẩn
Mọi chương trình Unix đều có ba kênh dữ liệu:
| Tên | Số | Vai trò |
|---|---|---|
| stdin | 0 | Đầu vào (mặc định: bàn phím) |
| stdout | 1 | Đầu ra bình thường (mặc định: màn hình) |
| stderr | 2 | Đầu ra lỗi (mặc định: màn hình) |
Điểm quan trọng: stdout và stderr là hai luồng riêng biệt, dù cùng hiện lên màn hình. Đây là lý do đôi khi bạn redirect output ra file mà lỗi vẫn hiện trên màn hình.
Chuyển hướng output
ls > out.txt # ghi đè file
ls >> out.txt # nối thêm vào cuối
ls 2> err.txt # chỉ ghi lỗi
ls > out.txt 2>&1 # cả output lẫn lỗi vào cùng một file
ls &> all.txt # viết tắt của dòng trên
ls > /dev/null # vứt bỏ output
ls 2> /dev/null # vứt bỏ lỗi — hay dùng để lệnh chạy im lặng
/dev/null là "hố đen" của hệ thống, mọi thứ ghi vào đó đều biến mất.
Cú pháp 2>&1 đọc là: "chuyển luồng 2 vào chỗ luồng 1 đang trỏ tới". Thứ tự quan trọng — 2>&1 > file không giống > file 2>&1.
Chuyển hướng input
sort < names.txt
mysql -u root mydb < backup.sql
Pipe — nối lệnh
cmd1 | cmd2 # output của cmd1 thành input của cmd2
Đây là ý tưởng mạnh nhất của Unix: mỗi lệnh làm tốt một việc, nối lại thành công cụ phức tạp.
ps aux | grep node
cat access.log | grep "500" | wc -l
ls -la | less
du -sh * | sort -hr | head -10
history | grep docker
Các lệnh hay đứng sau pipe
grep <pattern> # lọc dòng khớp
grep -v <pattern> # lọc dòng KHÔNG khớp
grep -i <pattern> # không phân biệt hoa thường
grep -c <pattern> # đếm số dòng khớp
grep -r <pattern> . # tìm đệ quy trong thư mục
sort # sắp xếp
sort -n # sắp theo số
sort -r # đảo ngược
sort -k3 # sắp theo cột 3
sort -h # hiểu 1K, 2M, 3G
uniq # bỏ dòng trùng LIỀN KỀ (nên sort trước)
uniq -c # đếm số lần lặp
wc -l # đếm dòng
wc -w # đếm từ
head -20 / tail -20 # lấy đầu / cuối
cut -d',' -f2 # cắt cột 2, phân cách bởi dấu phẩy
tr 'a-z' 'A-Z' # chuyển đổi ký tự
awk '{print $1}' # in cột 1
sed 's/cu/moi/g' # thay thế text
Ví dụ thực tế
# Top 10 IP truy cập nhiều nhất trong access log
cat access.log | awk '{print $1}' | sort | uniq -c | sort -rn | head -10
# Đếm lỗi 500 hôm nay
grep "$(date +%Y-%m-%d)" app.log | grep -c "500"
# Tìm TODO trong code, bỏ node_modules
grep -rn "TODO" --include="*.js" . | grep -v node_modules
# Xem 10 project chiếm ổ đĩa nhất
du -sh ~/projects/* | sort -hr | head -10
tee — vừa xem vừa lưu
npm run build | tee build.log # hiện trên màn hình VÀ ghi vào file
npm run build | tee -a build.log # nối thêm thay vì ghi đè
Chương 7 — Expansion, cách shell nhìn thấy lệnh
Trước khi thực thi, shell biến đổi dòng lệnh bạn gõ. Hiểu bước này giúp tránh vô số lỗi khó hiểu.
Kiểm chứng bằng echo — nó in ra chính xác thứ shell đã biến đổi:
echo * # in ra tên tất cả file, không phải dấu sao
Các loại expansion
1. Pathname (wildcard)
echo *.js
echo ~/Downloads/*
2. Tilde
echo ~ # /Users/bizzidev
3. Arithmetic
echo $((2 + 3)) # 5
echo $((10 / 3)) # 3 — chỉ số nguyên
4. Brace
echo {1..5} # 1 2 3 4 5
echo file{A,B,C}.txt # fileA.txt fileB.txt fileC.txt
mkdir -p project/{src,dist,test}
5. Parameter (biến)
echo $HOME
echo $USER
echo $PATH
echo ${HOME}/projects # dùng {} khi cần ngăn cách rõ ràng
6. Command substitution — quan trọng nhất
echo "Hom nay la $(date)"
kill $(lsof -i :3000 -t)
cd $(dirname $(which node))
$(lệnh) chạy lệnh bên trong trước rồi thay bằng kết quả. Cú pháp cũ là dấu backtick `lệnh` — dùng $() vì lồng nhau được.
Quoting — vấn đề gây lỗi nhiều nhất
Ba mức độ:
FILE="my file.txt"
echo $FILE # my file.txt — nhưng shell tách thành 2 tham số
echo "$FILE" # my file.txt — giữ nguyên là 1 tham số
echo '$FILE' # $FILE — không thay biến gì cả
| Kiểu | Biến $VAR | Wildcard * | $(lệnh) |
|---|---|---|---|
| Không nháy | Có thay | Có khớp | Có chạy |
Nháy kép " | Có thay | Không | Có chạy |
Nháy đơn ' | Không | Không | Không |
Quy tắc thực dụng: luôn bọc biến trong nháy kép. Đây là nguồn bug số một trong shell script:
rm -rf $DIR/ # NGUY HIỂM: nếu DIR rỗng → rm -rf /
rm -rf "$DIR"/ # An toàn hơn
Test nhanh sự khác biệt:
touch "file co dau cach.txt"
FILE="file co dau cach.txt"
ls $FILE # lỗi: tìm 4 file riêng lẻ
ls "$FILE" # đúng
rm "$FILE"
Chương 8 — Mẹo bàn phím
Phần này tăng tốc độ làm việc nhiều hơn bất kỳ chương nào khác.
Tab completion
Gõ vài ký tự rồi bấm Tab:
- Một kết quả khớp → tự điền nốt
- Nhiều kết quả → bấm
Tabhai lần để xem danh sách
Hoạt động với tên lệnh, tên file, tên biến, và cả tham số của nhiều lệnh (git, npm, docker).
Đây cũng là cách chống gõ sai đường dẫn — nếu Tab không tự điền, tức là đường dẫn đó không tồn tại.
Di chuyển con trỏ
| Phím | Tác dụng |
|---|---|
Ctrl+A | Về đầu dòng |
Ctrl+E | Về cuối dòng |
Option+←/→ | Nhảy từng từ |
Ctrl+U | Xóa từ con trỏ về đầu dòng |
Ctrl+K | Xóa từ con trỏ tới cuối dòng |
Ctrl+W | Xóa một từ phía trước |
Ctrl+L | Xóa màn hình (giống clear) |
Ctrl+Y | Dán lại đoạn vừa xóa |
Ctrl+U hay dùng nhất: gõ nửa lệnh thấy sai, bấm một phát xóa sạch, gõ lại.
Tìm trong lịch sử
| Phím | Tác dụng |
|---|---|
↑ / ↓ | Duyệt lệnh trước/sau |
Ctrl+R | Tìm ngược trong lịch sử — gõ vài ký tự, nó tự lọc |
Ctrl+R lần nữa | Tới kết quả cũ hơn |
Enter | Chạy |
→ hoặc Ctrl+E | Lấy lệnh ra để sửa trước khi chạy |
Ctrl+C | Hủy tìm |
Ctrl+R là mẹo đáng giá nhất cả chương. Thay vì bấm ↑ ba mươi lần, gõ Ctrl+R rồi docker là ra ngay lệnh docker gần nhất.
Điều khiển tiến trình đang chạy
| Phím | Tác dụng |
|---|---|
Ctrl+C | Dừng chương trình đang chạy (gửi SIGINT) |
Ctrl+Z | Tạm dừng, đưa xuống nền |
Ctrl+D | Báo hết input / thoát shell |
Ctrl+S / Ctrl+Q | Khóa / mở khóa màn hình (nếu terminal đơ bất ngờ, thử Ctrl+Q) |
Tái sử dụng tham số
mkdir /path/rat/dai/toi/project
cd !$ # !$ = tham số cuối của lệnh trước
sudo !! # chạy lại lệnh vừa rồi với sudo
Chương 9 — Permission
Vì sao có permission
Unix thiết kế cho nhiều người dùng chung một máy. Permission đảm bảo user này không đọc/sửa/xóa được dữ liệu của user kia.
Mỗi user có UID, thuộc một hoặc nhiều group.
whoami # tên user
id # UID, GID, tất cả group
groups # chỉ liệt kê group
Đọc chuỗi quyền
drwxr-xr-x
│└┬┘└┬┘└┬┘
│ │ │ └── others: mọi người còn lại
│ │ └───── group: thành viên group sở hữu
│ └──────── owner: chủ sở hữu
└────────── loại: d=thư mục, -=file, l=symlink
Ba quyền:
| Ký tự | Với file | Với thư mục |
|---|---|---|
r read | Đọc nội dung | ls được |
w write | Sửa nội dung | Tạo/xóa file bên trong |
x execute | Chạy như chương trình | cd vào được |
Cột thư mục là chỗ hay nhầm: có r mà không x thì thấy tên file nhưng không vào được; có x mà không r thì vào được nhưng không liệt kê được.
Ký hiệu số
r = 4 w = 2 x = 1
| Số | Chuỗi | Nghĩa |
|---|---|---|
| 7 | rwx | đủ quyền |
| 6 | rw- | đọc ghi |
| 5 | r-x | đọc và vào |
| 4 | r-- | chỉ đọc |
| 0 | --- | không gì |
Bộ số hay gặp:
| Bộ | Dùng cho |
|---|---|
755 | Thư mục, file thực thi |
644 | File thường |
700 | Home directory — chỉ mình đọc |
600 | .env, SSH private key |
400 | Chỉ đọc, chống sửa nhầm |
700 chính là lý do bạn không đọc được home của user khác trên cùng máy.
Thay đổi quyền
chmod 644 file.txt # dạng số
chmod +x script.sh # thêm quyền chạy cho tất cả
chmod u+x script.sh # chỉ owner (u=user, g=group, o=others, a=all)
chmod go-w file.txt # bỏ quyền ghi của group và others
chmod -R 755 mydir/ # đệ quy
sudo chown user file # đổi owner
sudo chown user:group file # đổi cả hai
Thứ tự kiểm tra
Hệ thống kiểm tra và dừng ở nhóm khớp đầu tiên:
- Bạn là owner? → dùng quyền owner
- Thuộc group sở hữu? → dùng quyền group
- Còn lại → dùng quyền others
Hệ quả lạ nhưng đúng: nếu owner là --- và others là rwx, chính owner không truy cập được trong khi người ngoài truy cập được.
sudo và root
root là user UID 0, vượt qua mọi kiểm tra quyền. macOS vô hiệu hóa tài khoản root; bạn mượn quyền qua sudo.
sudo <lệnh> # chạy với quyền root
sudo -l # xem mình được phép sudo gì
sudo chỉ dùng được nếu user thuộc group được cấp phép:
| Hệ | Group |
|---|---|
| macOS | admin |
| Ubuntu/Debian | sudo |
| RHEL/CentOS | wheel |
Kiểm tra:
groups | grep -q admin && echo "co sudo" || echo "khong co sudo"
Không thuộc group đó thì sudo báo <user> is not in the sudoers file.
Lỗi permission thường gặp với Node
Script không chạy được
./deploy.sh
# permission denied
chmod +x deploy.sh
npm install -g báo EACCES
npm install -g pm2
# EACCES: permission denied, mkdir '/usr/local/lib/node_modules'
Nguyên nhân: /usr/local thuộc root. Cách đúng: đừng dùng sudo npm — cài nvm để Node nằm hẳn trong home, hết đụng quyền.
SSH key bị từ chối
chmod 700 ~/.ssh
chmod 600 ~/.ssh/id_rsa
SSH cố tình từ chối làm việc nếu key có quyền quá rộng.
File .env lộ
ls -l .env # -rw-r--r-- → others đọc được
chmod 600 .env
Chương 10 — Process
Khái niệm
Process là một chương trình đang chạy. Mỗi process có:
- PID — số định danh duy nhất
- PPID — PID của process cha
- User sở hữu — quyết định nó được làm gì
- Trạng thái — chạy, ngủ, dừng, zombie
Mọi process sinh ra từ một process khác, tạo thành cây. Gốc cây PID 1 là launchd (macOS) hoặc systemd (Linux).
Xem process
ps aux # tất cả process của mọi user
ps aux | grep node # lọc theo tên
pgrep -fl node # gọn hơn: PID + tên
pgrep -u bizzidev -fl node # lọc thêm theo user
Giải thích aux: a = mọi user, u = hiện tên user, x = gồm cả process không gắn terminal.
Đọc ps aux
USER PID %CPU %MEM RSS STAT STARTED COMMAND
bizzidev 4661 0.5 1.2 95632 S 20:15 node server.js
| Cột | Ý nghĩa |
|---|---|
| USER | Ai sở hữu — quyết định bạn kill được không |
| PID | Số để kill |
| %CPU / %MEM | Tài nguyên chiếm |
| RSS | RAM thực tế (KB) |
| STAT | S ngủ, R chạy, Z zombie, T dừng |
| COMMAND | Lệnh đầy đủ |
Vì sao cần grep -v grep
ps aux | grep node | grep -v grep
Chính lệnh grep node bạn vừa gõ cũng là một process, và trong tên nó có chữ "node" — nó tự tìm thấy mình. grep -v grep dọn dòng nhiễu đó.
Xem realtime
top -o cpu # sắp theo CPU, q để thoát
top -o mem # sắp theo RAM
htop # dễ nhìn hơn (brew install htop)
Tín hiệu và kill
kill 4661 # SIGTERM: xin dừng lịch sự
kill -9 4661 # SIGKILL: ép chết ngay
pkill -f "node server.js" # kill theo tên
killall node # kill mọi process tên node
SIGTERM (kill) | SIGKILL (kill -9) | |
|---|---|---|
| Process biết không | Có | Không |
| Kịp dọn dẹp | Có: đóng DB, flush log | Không |
| Có thể bị từ chối | Có | Không bao giờ |
| Khi nào dùng | Mặc định | Chỉ khi treo hẳn |
Đây là lý do app Node nên xử lý graceful shutdown:
process.on('SIGTERM', async () => {
await server.close();
await db.disconnect();
process.exit(0);
});
Docker và Kubernetes đều gửi SIGTERM trước, chờ vài giây, rồi mới SIGKILL. App không xử lý SIGTERM sẽ mất request đang dở mỗi lần deploy.
Quyền kill
Bạn chỉ kill được process của chính mình:
kill: 46618: Operation not permitted
Muốn kill process user khác phải có root, mà sudo lại đòi thuộc group admin. Đây là chỗ ba khái niệm user, permission, process gặp nhau.
Lưu ý: ps aux là lệnh chỉ đọc nên user thường vẫn xem được process của mọi user — chỉ không tắt được thôi.
Job control
node server.js & # chạy nền
jobs # liệt kê job của terminal này
fg %1 # đưa job 1 lên foreground
bg %1 # cho job 1 chạy tiếp ở nền
# Ctrl+Z → tạm dừng, rồi bg để cho chạy nền
Process chạy bằng & sẽ chết khi đóng terminal. Muốn sống tiếp:
nohup node server.js & # bỏ qua tín hiệu ngắt kết nối
Thực tế nên dùng pm2 hoặc systemd thay vì nohup.
Port — phần dev đụng nhiều nhất
lsof -i :3000 # ai đang giữ port 3000
lsof -i -P | grep LISTEN # tất cả port đang lắng nghe
lsof -i :3000 -t # chỉ ra PID
Xử lý EADDRINUSE:
lsof -i :3000 # xem thủ phạm
kill $(lsof -i :3000 -t) # giải phóng trong một dòng
-P giữ số port thay vì dịch thành tên service.
Service nền của hệ thống
macOS (launchd) | Linux (systemd) |
|---|---|
launchctl list | systemctl list-units |
launchctl start <name> | systemctl start <name> |
~/Library/LaunchAgents | /etc/systemd/system |
launchctl list | grep -i docker
sudo launchctl list | grep docker
Bảng tra nhanh
Navigation
pwd # đang ở đâu
cd - # quay lại thư mục trước
ls -lah # chi tiết + file ẩn + size dễ đọc
ls -ld <dir> # thông tin của chính thư mục
ls -lt # file nào vừa sửa gần nhất
File
mkdir -p a/b/c # tạo cả cây
cp -r src/ dest/ # copy đệ quy
mv old new # đổi tên / di chuyển
rm -i *.log # xóa có hỏi
ln -s target link # tạo symlink
find . -name "*.log" # tìm file
du -sh * | sort -hr # thư mục nào nặng nhất
Lệnh
which -a node # tất cả bản node trong PATH
type ls # ls là loại lệnh gì
echo $PATH # danh sách thư mục tìm lệnh
echo $? # exit code lệnh vừa rồi
cmd1 && cmd2 # chạy cmd2 nếu cmd1 thành công
Redirection
cmd > file # ghi đè
cmd >> file # nối thêm
cmd 2>/dev/null # vứt lỗi đi
cmd &> all.log # cả output lẫn lỗi
cmd | tee file # vừa xem vừa lưu
Text
grep -rn "TODO" . # tìm đệ quy, kèm số dòng
grep -v pattern # loại bỏ dòng khớp
sort | uniq -c # đếm số lần lặp
wc -l # đếm dòng
tail -f app.log # theo dõi log realtime
awk '{print $1}' # in cột 1
Permission
id # tôi thuộc group nào
chmod +x script.sh # cho phép chạy
chmod 600 .env # khóa file nhạy cảm
chmod 700 ~/.ssh # quyền chuẩn cho SSH
sudo chown user file # đổi chủ sở hữu
Process
pgrep -fl node # tìm process
top -o cpu # realtime
kill <pid> # dừng lịch sự
kill -9 <pid> # ép dừng
lsof -i :3000 # ai giữ port
kill $(lsof -i :3000 -t) # giải phóng port
Phím tắt đáng nhớ nhất
Ctrl+R tìm trong lịch sử lệnh
Ctrl+A/E đầu / cuối dòng
Ctrl+U xóa cả dòng đang gõ
Ctrl+C dừng chương trình
Tab tự hoàn thành
!! lệnh vừa rồi
!$ tham số cuối của lệnh trước
Học tiếp
Sau khi thạo 10 chương này:
Chương 11-16 của TLCL: biến môi trường, cấu hình shell, trình soạn thảo vi, prompt tùy chỉnh, quản lý gói, lưu trữ và sao lưu.
Chương 24-36: shell scripting — viết script tự động hóa, vòng lặp, hàm, xử lý lỗi.
Thực hành có giá trị nhất: thuê VPS ~5$/tháng, tự tay tạo user, phân quyền sudo, deploy app Node, cấu hình nginx reverse proxy, systemd service, SSL. Cố tình phá rồi sửa. Một tháng làm việc này bằng nửa năm đọc lý thuyết.
Sau đó học Docker. Lúc này bạn sẽ hiểu container thực chất chỉ là process + namespace + cgroup của Linux, không phải "máy ảo nhẹ" như cách người ta hay mô tả.
Tài nguyên
| Nguồn | Dùng khi |
|---|---|
| linuxcommand.org | Bản gốc TLCL, đọc free |
| explainshell.com | Dán lệnh lạ vào, tách nghĩa từng flag |
man <lệnh> | Tra nhanh trong terminal |
tldr <lệnh> | Bản man rút gọn, chỉ ví dụ (brew install tldr) |
| MIT Missing Semester | Khóa free về shell, git internals, debugging |
| shellcheck.net | Kiểm lỗi shell script trước khi chạy |