Chương 03 — Pipes & Redirects
Đây là chương mà CLI thật sự phát huy sức mạnh. Học cách ghép nhiều lệnh nhỏ
thành một chuỗi xử lý dữ liệu — đếm dòng log, lọc process, gộp file, lưu output ra disk.
Mỗi công cụ làm một việc, nhưng khi nối lại bằng | và > thì gần như không có gì là không làm được.
1. stdout, stderr, stdin — 3 stream chuẩn
Mỗi process trên Unix khi chạy đều có 3 luồng dữ liệu mặc định, được đánh số bằng file descriptor (fd):
| fd | Tên | Vai trò | Mặc định nối tới |
|---|---|---|---|
0 | stdin | Input — nơi lệnh đọc dữ liệu vào | Bàn phím |
1 | stdout | Output bình thường | Màn hình terminal |
2 | stderr | Output lỗi / cảnh báo | Màn hình terminal |
Hai stream output (1 và 2) cùng đổ ra terminal nên trông giống nhau, nhưng chúng tách biệt — và đó là điều quan trọng nhất chương này.
$ ls existing.txt missing.txt
existing.txt # dòng này là stdout
ls: missing.txt: No such file or directory # dòng này là stderr
2. Đọc file: cat, less, head, tail
cat — concatenate & in nội dung
$ cat README.md # in toàn bộ file ra stdout
$ cat a.txt b.txt # nối nhiều file (đó là tên gốc: con-CAT-enate)
$ cat -n script.sh # -n: kèm số dòng
cat phù hợp cho file ngắn. File dài (vài nghìn dòng) sẽ tràn màn hình — dùng less.
less — đọc file dài có thể scroll
$ less /var/log/system.log # mở pager
Khi đang trong less, các phím hữu ích:
↑ ↓hoặcj k— cuộn từng dòngSpace— cuộn 1 trang xuống,b— 1 trang lên/từ-khoárồi Enter — search;nđể tới match tiếp theog— về đầu file,G— xuống cuốiq— thoát (đừng quên!)
head & tail — xem đầu / cuối file
$ head access.log # 10 dòng đầu (mặc định)
$ head -n 20 access.log # 20 dòng đầu
$ tail -n 50 error.log # 50 dòng cuối
$ tail -f app.log # follow: theo dõi log realtime, dòng mới hiện ngay
tail -f là vũ khí số 1 của DevOpstail -f app.log ở 1 cửa sổ, restart app ở cửa sổ khác — bạn thấy log chạy realtime. Kết hợp với grep ở Chương 4: tail -f app.log | grep ERROR — chỉ hiện dòng có chữ ERROR.
3. Đếm & gộp: wc, sort, uniq, tee
wc — word count (line / word / char)
$ wc README.md
42 180 1024 README.md # lines words bytes filename
$ wc -l *.js # -l: chỉ đếm dòng — đếm SLOC nhanh
$ wc -w essay.txt # -w: đếm từ
$ wc -c file.bin # -c: đếm byte
sort — sắp xếp dòng
$ sort names.txt # A→Z
$ sort -r names.txt # reverse: Z→A
$ sort -n sizes.txt # -n: sort theo số (tự nhiên), không phải string
$ sort -u dup.txt # -u: unique, bỏ trùng
uniq — bỏ dòng trùng liền kề
Quan trọng: uniq chỉ gộp dòng trùng kề nhau. Vì vậy luôn đi cặp với sort trước:
$ sort log.txt | uniq # gộp dòng trùng
$ sort log.txt | uniq -c # -c: kèm count mỗi dòng xuất hiện bao nhiêu lần
$ sort log.txt | uniq -d # -d: chỉ in dòng trùng (duplicate)
tee — chia stream thành 2
Hình chữ T trong tên gọi: input đi vào, output chia làm 2 hướng — vừa ra stdout vừa ghi file.
$ ls | tee filelist.txt # vừa hiện trên màn hình, vừa lưu file
$ make | tee build.log # xem build live, đồng thời lưu log để xem lại
$ echo "127.0.0.1 dev" | sudo tee -a /etc/hosts # -a: append; pattern phổ biến để ghi file cần sudo
4. Pipe | — nối stdout của lệnh A vào stdin của lệnh B
Đây là linh hồn của Unix philosophy: "Mỗi chương trình làm 1 việc, làm thật tốt. Nối chúng bằng pipe để giải bài toán lớn."
$ ls | wc -l # đếm số file/folder trong thư mục hiện tại
$ ps aux | grep node # tìm mọi process node đang chạy
$ history | tail -n 20 # 20 lệnh gần nhất bạn đã gõ
Ví dụ kinh điển — top 10 IP truy cập nhiều nhất từ access log
$ cat access.log | awk '{print $1}' | sort | uniq -c | sort -rn | head -n 10
Đọc từ trái qua phải, mỗi pipe là một bước:
cat access.log— đọc file logawk '{print $1}'— lấy cột 1 (IP address)sort— sắp xếp để các IP giống nhau nằm cạnh nhauuniq -c— gộp + đếm số lầnsort -rn— sort theo số, giảm dầnhead -n 10— lấy 10 dòng đầu
5. Redirect: > >> < 2> &>
Ghi stdout ra file: > và >>
$ ls > files.txt # ghi đè (overwrite): nội dung cũ MẤT
$ date >> log.txt # append: nối thêm vào cuối
$ echo "Hello" > greet.txt # tạo file mới với nội dung "Hello"
> ghi đè không hỏicat important.txt > important.txt sẽ xóa sạch file. Shell mở file để ghi (truncate về rỗng) trước khi chạy cat. Nếu cần append, luôn dùng >>. Để an toàn hơn, có thể bật set -o noclobber trong shell — khi đó > sẽ từ chối ghi đè file đã tồn tại.
Đọc file làm stdin: <
$ wc -l < access.log # tương đương wc -l access.log nhưng đi qua stdin
$ sort < names.txt > sorted.txt # đọc names.txt, sort, ghi ra sorted.txt
Redirect stderr: 2>
$ make 2> errors.log # stderr → file, stdout vẫn ra màn hình
$ find / -name "*.conf" 2> /dev/null # nuốt mọi "Permission denied"
Gộp cả stdout và stderr
$ make > build.log 2>&1 # stdout → file, stderr → cùng nơi với stdout
$ make &> build.log # shortcut: stdout + stderr cùng vào file (bash/zsh)
2>&1 rất quan trọngcmd > file 2>&1 đúng: stdout sang file, rồi stderr theo sau stdout (cũng vào file).
cmd 2>&1 > file SAI: stderr trỏ tới chỗ stdout đang trỏ (lúc đó vẫn là màn hình), rồi stdout mới đổi sang file. Kết quả: stderr ra màn hình, chỉ stdout vào file.
/dev/null — "thùng rác" của hệ thống
$ cmd > /dev/null # vứt stdout đi, chỉ giữ stderr để debug
$ cmd 2> /dev/null # vứt stderr, giữ output
$ cmd > /dev/null 2>&1 # im lặng tuyệt đối — chỉ quan tâm exit code
Bảng tổng kết redirect
| Cú pháp | Ý nghĩa |
|---|---|
> file | stdout → file (ghi đè) |
>> file | stdout → file (append) |
< file | file → stdin |
2> file | stderr → file |
2>> file | stderr → file (append) |
2>&1 | stderr → đi cùng stdout |
&> file | cả stdout + stderr → file |
> /dev/null | vứt output |
6. Here-doc, Here-string & Process substitution
Here-document <<EOF — nhúng nhiều dòng làm stdin
$ cat > config.yml <<EOF
server:
host: localhost
port: 8080
EOF
Mọi thứ giữa <<EOF và dòng EOF được dùng làm stdin. EOF chỉ là quy ước — đặt tên gì cũng được, miễn nhất quán giữa mở và đóng.
Here-string <<< — 1 chuỗi ngắn làm stdin
$ wc -w <<< "hello world from cli"
4
$ grep apple <<< "I like apple pie"
I like apple pie
Process substitution <(cmd)
Cho phép output của một lệnh được dùng như một file ảo. Cực kỳ tiện cho các lệnh chỉ nhận file (như diff):
$ diff <(ls dir1) <(ls dir2) # so sánh nội dung 2 thư mục
$ diff <(sort a.txt) <(sort b.txt) # so sánh sau khi sort
$ comm <(sort emails-2024.txt) <(sort emails-2025.txt)
Hướng ngược >(cmd) ít gặp hơn — gửi output đến nhiều consumer cùng lúc, ví dụ echo data | tee >(gzip > out.gz) >(wc -c).
7. Thử ngay trong terminal giả lập
Simulator đã hỗ trợ pipe và redirect cơ bản — thử các kịch bản:
- Tạo file demo:
echo "apple" > fruits.txt - Append thêm:
echo "banana" >> fruits.txtrồiecho "apple" >> fruits.txt - Xem nội dung:
cat fruits.txt - Đếm dòng:
cat fruits.txt | wc -l - Đếm tần suất:
sort fruits.txt | uniq -c - Lưu kết quả:
sort fruits.txt | uniq -c > report.txt - Xem report:
cat report.txt
8. Bài tập về nhà
Trên Terminal thật, làm tuần tự (xong báo lại tôi check):
- Đếm tổng số dòng trong tất cả file
.jsở thư mục dự án (gợi ý:wc -l *.js— dòng cuối là tổng). - Tạo file
names.txtchứa 5 cái tên (mỗi dòng 1 tên, có vài tên trùng). Đếm xem mỗi tên xuất hiện bao nhiêu lần — chỉ dùngsort+uniq -c. - Chạy
ls /etcvà lưu kết quả vàoetc-listing.txt. Sau đó dùngwc -lđể biết/etccó bao nhiêu mục. - Chạy
find / -name "*.log" 2> /dev/null | head. Giải thích vì sao có2> /dev/nulltrong lệnh. - Cùng lệnh
ls -la: ghi output ra filelist.txtnhưng vẫn hiện trên màn hình. Dùng lệnh nào? - Tạo
config.ini(3 dòng nội dung) chỉ bằng 1 lệnh dùng here-document<<EOF. - So sánh nội dung 2 thư mục
~/Downloadsvà~/Desktopbằngdiff+ process substitution. - Trick câu hỏi: chạy
cmd 2>&1 > out.txtvàcmd > out.txt 2>&1. Output (stderr) của 2 cách rơi vào đâu? Vì sao khác nhau? - Bonus: tạo 1 chuỗi pipe đếm 5 lệnh bạn dùng nhiều nhất từ history (gợi ý:
history | awk '{print $2}' | sort | uniq -c | sort -rn | head -n 5).
9. Quiz — Kiểm tra kiến thức
10. Tổng kết
- ✅ Hiểu 3 stream: stdin (0), stdout (1), stderr (2)
- ✅ Đọc file:
cat,less,head,tail -fđể theo dõi log realtime - ✅ Đếm & thống kê:
wc -l,sort,uniq -c,tee - ✅ Pipe
|— nối stdout của lệnh trước vào stdin lệnh sau - ✅ Redirect:
>ghi đè,>>append,<đọc từ file - ✅ Tách / gộp stderr:
2>,2>&1,&>,/dev/null - ✅ Here-doc
<<EOF, here-string<<<, process substitution<(cmd)
Chương 4 chúng ta sẽ học search — grep, find và fzf — bộ ba để tìm bất cứ thứ gì trong file system và nội dung file. Kết hợp với pipe của chương này, bạn sẽ có superpower thật sự.