
Linux Essentials 5.4: Các Thư mục và Tệp đặc biệt
Hiểu thư mục tạm và tệp đặc biệt Linux; thử mktemp, sticky bit, hard link và symlink, kiểm tra đích sau di chuyển và phân biệt liên kết với backup.

Thực hành Linux với ticket giả lập: tạo script đếm, kiểm tra lỗi, giới hạn dữ liệu, phục hồi TAR và xác minh báo cáo HTTP loopback bằng so sánh byte.
Bạn nhận một danh sách ticket giả lập và cần thống kê các chủ đề còn mở. Bài thực hành này nối những kiến thức đã học thành một quy trình: xác định môi trường, xử lý đường dẫn có khoảng trắng, viết script, giới hạn vùng phục vụ HTTP, tạo archive rồi thử khôi phục vào đích riêng. Tiêu chí đạt nằm ở dữ liệu đầu ra và bằng chứng, không chỉ việc lệnh trả về thành công.
Đây là bài tổng hợp tự biên soạn, không phải một mục tiêu thi bổ sung. Bạn nên hoàn thành các bài Linux trước đó, đặc biệt shell script, lưu trữ, mạng và quyền tệp. Chuẩn bị Bash, tar, gzip, sort, uniq, cmp, Python 3, curl và ss trong lab riêng. Bài không hướng dẫn cài gói trên máy làm việc.
Mốc kiểm chứng: 23/10/2026. Toàn bộ chuỗi đã chạy cùng nhau bằng tài khoản không root trên Ubuntu 26.04.1 LTS ARM64 trong container network-none, không volume host. Binary gồm Bash 5.3.9, GNU tar 1.35, curl 8.18.0 có bản vá distro và Python 3.14.4. Một số tiện ích coreutils là uutils 0.10.0; không suy ra mọi lệnh đều thuộc GNU.
| Đầu vào hoặc đầu ra | Điều kiện |
|---|---|
| Ticket | Dữ liệu giả, mỗi dòng ba trường đơn giản |
| Báo cáo | Chỉ đếm chủ đề có state bằng open |
| Script | Thiếu tham số trả 2, tệp không hợp lệ trả 3 |
| Archive | Tạo từ những thành phần đã chỉ định |
| Phục hồi | Vào thư mục riêng, so byte và tạo lại báo cáo |
| HTTP | Chỉ phục vụ thư mục export trên loopback |
Định dạng đầu vào cố ý đơn giản: không có dấu phẩy, dòng mới hoặc dấu nháy bên trong một trường. Script phía dưới không phải bộ phân tích CSV tổng quát. Nếu dữ liệu thực tế có trường được quote, cần dùng parser phù hợp và đặt quy tắc validation riêng.
Không dùng ticket khách hàng, credential hoặc thông tin cá nhân. Bài cũng không thực hiện quét mạng, cài VM, kiểm thử systemd hay xác thực TLS. Loopback trong container chỉ xác nhận giao tiếp của môi trường đó.

id
cat /etc/os-release
bash --version
tar --version
umask 077
work=$(mktemp -d)
mkdir "$work/input data" "$work/export" "$work/restore"
Kiểm tra UID không phải 0 trước khi tiếp tục. umask 077 giới hạn quyền mặc định trong phiên lab; nếu không muốn đổi phiên đang dùng, mở shell riêng cho bài. mktemp tạo đường dẫn mới. Giữ biến work trong suốt lần chạy, không lấy một đường dẫn từ ảnh hoặc từ phiên cũ.
Thư mục input data có khoảng trắng để bạn phải dùng quoting đúng. export là nơi duy nhất sẽ được phục vụ HTTP. restore nằm ngoài đầu vào và sẽ dùng cho phục hồi. Sự phân chia này giảm nguy cơ vô tình công khai dữ liệu thô trong phép thử, nhưng chưa phải mô hình kiểm soát truy cập của web server production.
cat > "$work/input data/tickets.csv" <<'DATA'
id,state,topic
T01,open,network
T02,closed,printer
T03,open,disk
T04,open,network
DATA
Đây là toàn bộ fixture. Không thêm dữ liệu khác ở lần đầu: cần một trường hợp có kết quả dự đoán rõ trước khi thử biến thể. Báo cáo phải có disk một lần và network hai lần; ticket printer đã closed nên không được đếm.
Tạo tệp script bằng heredoc có delimiter được quote:
cat > "$work/report.sh" <<'SCRIPT'
#!/bin/bash
if [ "$#" -ne 1 ]; then
printf '%s\n' 'Usage: report.sh INPUT' >&2
exit 2
fi
if [ ! -f "$1" ] || [ ! -r "$1" ]; then
printf '%s\n' 'Input must be a readable regular file' >&2
exit 3
fi
while IFS=, read -r ticket state topic; do
if [ "$state" = open ]; then
printf '%s\n' "$topic"
fi
done < "$1" | LC_ALL=C sort | uniq -c
SCRIPT
Quote ở <<'SCRIPT' giữ các biến trong nội dung để script xử lý khi chạy, thay vì mở rộng chúng ngay lúc tạo tệp. Nhánh lỗi gửi thông báo tới stderr, còn stdout dành cho báo cáo. Điều đó giúp redirect kết quả không trộn thông báo lỗi vào dữ liệu.
Vòng lặp đọc theo fixture, lọc chính xác giá trị state. sort đưa các chủ đề giống nhau về cạnh nhau trước uniq -c; chỉ dùng uniq trực tiếp chưa chắc đếm được các dòng giống nhau nằm cách nhau. LC_ALL=C làm thứ tự sắp xếp trong phép thử dễ đối chiếu hơn giữa các locale.
bash -n "$work/report.sh"
chmod u+x "$work/report.sh"
"$work/report.sh" "$work/input data/tickets.csv" \
> "$work/export/report.txt"
cat "$work/export/report.txt"
bash -n kiểm tra cú pháp nhưng không chạy các nhánh để chứng minh logic. Đầu ra mong đợi có hai chủ đề, disk đếm 1 và network đếm 2. Số khoảng trắng trước count thuộc cách trình bày của công cụ; người học cần đọc giá trị và thứ tự, còn bộ kiểm thử đã so chính xác fixture với binary đang dùng.
Thử nhánh thất bại rồi đọc mã ngay sau lệnh:
"$work/report.sh"
printf 'exit=%s\n' "$?"
"$work/report.sh" "$work/missing.csv"
printf 'exit=%s\n' "$?"
Mong đợi lần lượt 2 và 3. Chạy các thử nghiệm này trong shell tương tác bình thường; nếu đang bật set -e, lệnh thất bại có thể kết thúc shell trước khi bạn in mã. Bộ kiểm thử dùng cấu trúc điều kiện để kiểm tra trạng thái thất bại có chủ đích.
Một fixture chỉ có ticket closed phải tạo báo cáo rỗng thành công, khác với tệp đầu vào bị thiếu. Bộ kiểm thử đã xác nhận trường hợp này. Tuy nhiên script vẫn là mẫu nhập môn: nó chưa phát hiện mọi dữ liệu sai schema, lỗi downstream hoặc dòng cuối thiếu ký tự newline. Không đưa nguyên mẫu vào pipeline nghiệp vụ mà bỏ các yêu cầu đó.
chmod 600 "$work/input data/tickets.csv"
chmod 644 "$work/export/report.txt"
tar -czf "$work/lpi-fixture.tar.gz" -C "$work" \
'input data' export report.sh
tar -tzf "$work/lpi-fixture.tar.gz"
Theo manual GNU tar, các chế độ tạo, liệt kê và giải nén là thao tác khác nhau. -C trong lệnh này đổi nơi đọc các thành phần được chọn; archive được đặt bên ngoài những thành phần đó để tránh tự đưa chính archive vào dữ liệu đang đóng gói.
Danh sách phải có thư mục đầu vào, export và script. Việc liệt kê chứng minh đọc được cấu trúc archive trong phép thử, chưa chứng minh phục hồi được toàn bộ dịch vụ. Archive có chứa dữ liệu thô nên phải được bảo vệ theo dữ liệu bên trong; nén gzip không tự mã hóa nó.
Mode 600 của dữ liệu thô không thay ranh giới web. Server ở bước sau chạy bằng chính người sở hữu nên vẫn có thể đọc dữ liệu đó nếu bạn phục vụ nhầm cây. Chọn document root đúng là một yêu cầu độc lập.
tar -xzf "$work/lpi-fixture.tar.gz" -C "$work/restore"
cmp "$work/input data/tickets.csv" \
"$work/restore/input data/tickets.csv"
cmp "$work/export/report.txt" \
"$work/restore/export/report.txt"
bash "$work/restore/report.sh" \
"$work/restore/input data/tickets.csv" > "$work/rebuilt.txt"
cmp "$work/export/report.txt" "$work/rebuilt.txt"
cmp trả 0 và không báo khác biệt khi các tệp giống byte. Đích restore phải là thư mục mới, riêng cho lần thử; không giải nén chồng lên dữ liệu nguồn. Tạo lại báo cáo bằng script phục hồi kiểm tra thêm khả năng dùng đầu vào sau phục hồi, ngoài việc đọc được archive.
Đây là bằng chứng về byte và phép xử lý fixture. Chưa nghiệm thu ACL, extended attributes, quyền owner giữa các máy, mã hóa, thời gian phục hồi hay bản sao ở thiết bị độc lập. Archive nằm cùng cây tạm không đáp ứng một chiến lược backup production. Hãy ghi đúng giới hạn khi bàn giao kết quả.

Python http.server phù hợp phép thử đơn giản và không được khuyến nghị làm server production. Chỉ phục vụ export:
python3 -m http.server 8080 --bind 127.0.0.1 \
--directory "$work/export" > "$work/http.log" 2>&1 &
server=$!
ss -ltn 'sport = :8080'
Nếu chưa thấy socket, đợi server khởi động rồi kiểm tra lại. Nếu cổng bận, đọc log và chọn môi trường lab sạch; không dừng tiến trình không thuộc bài. Không bind 0.0.0.0 để làm cho ví dụ “dễ truy cập” từ nơi khác.
curl --noproxy '*' --connect-timeout 2 --max-time 5 \
-fsS http://127.0.0.1:8080/report.txt -o "$work/received.txt"
cmp "$work/export/report.txt" "$work/received.txt"
curl --noproxy '*' --connect-timeout 2 --max-time 5 \
-sS -o "$work/missing.html" -w '%{http_code}\n' \
http://127.0.0.1:8080/tickets.csv
Manual curl mô tả timeout, redirect đầu ra và xử lý HTTP lỗi. Báo cáo tải về phải giống byte; đường dẫn tickets.csv trả 404 trong fixture. Hai kết quả kiểm tra hai yêu cầu cụ thể, không chứng minh không có lỗ hổng hay dữ liệu nhạy cảm ở mọi đường dẫn. --noproxy chỉ dành cho phép thử loopback này, không phải hướng dẫn bỏ chính sách proxy tổ chức.
Dừng đúng tiến trình đã tạo khi xong:
kill -TERM "$server"
wait "$server"
Kiểm tra PID còn thuộc server lab trước khi gửi tín hiệu nếu phiên đã để lâu. Không cần nâng quyền để thao tác tiến trình của mình. Bộ kiểm thử dùng cleanup trap để dừng server khi có lỗi; bài tương tác yêu cầu bạn chủ động hoàn tất bước dọn tiến trình.
Bạn đạt bài khi có báo cáo đúng số đếm, mã lỗi đúng trường hợp, phục hồi riêng so byte thành công, báo cáo tái tạo giống bản đầu và HTTP chỉ trả nội dung fixture dự định. Nếu một gate thất bại, khoanh vùng tại bước đó trước khi tiếp tục; không sửa dữ liệu để ép khớp kết quả mong đợi.
Ghi ngắn gọn môi trường, lệnh, kết quả, nguyên nhân sai đã tìm được và điều chưa thử. Nếu quoting sai làm tệp không tìm thấy, giải thích cách shell chia đối số. Nếu HTTP 404, xem document root và tài nguyên thay vì kết luận mất mạng. Nếu phục hồi khác byte, giữ cả hai bản để điều tra thay vì ghi đè nguồn.
Bài tổng hợp kết thúc nhánh Linux ở mức nhập môn. Nó kiểm tra một quy trình có thể giải thích và tái hiện, không cấp bằng chứng rằng bạn đã quản trị VM, bảo vệ máy chủ hoặc đáp ứng toàn bộ yêu cầu thi. Các bài tiếp theo chuyển sang nhận diện tài sản, rủi ro và biện pháp bảo vệ trong Security Essentials.

Hiểu thư mục tạm và tệp đặc biệt Linux; thử mktemp, sticky bit, hard link và symlink, kiểm tra đích sau di chuyển và phân biệt liên kết với backup.

Đọc quyền tệp Linux, owner và group; thử chmod, chown, search thư mục và umask bằng tài khoản không root, phân biệt ghi nội dung với xóa tên tệp.

Tạo người dùng Linux và nhóm trong lab riêng; kiểm tra home, skeleton, nhóm bổ sung, khóa mật khẩu và phạm vi xóa tài khoản trước khi áp dụng thực tế.

Hiểu người dùng Linux qua UID, GID và tài khoản dịch vụ; thực hành quyền sudo giới hạn, xác thực SSH và kiểm tra host key trong lab cô lập riêng.