
Thực hành tổng hợp Linux Essentials: dữ liệu, script và quyền
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.

Viết shell script Bash đọc ticket giả lập, kiểm tra tham số và đường dẫn, dùng vòng lặp, tách stdout với stderr và kiểm chứng trạng thái trả về.
Sau khi lọc được ticket bằng lệnh tương tác, bạn có thể muốn dùng lại quy trình vào ngày hôm sau. Chép các lệnh vào một tệp là bước khởi đầu, nhưng script còn cần biết đầu vào nằm ở đâu, xử lý thế nào khi thiếu tệp và thông báo thành công có ý nghĩa gì. Nếu thiếu những điều này, tự động hóa chỉ giúp lặp lại nhầm lẫn nhanh hơn.
Bài thuộc mục tiêu 3.3 của Linux Essentials 010-160, liên hệ 013.3 của 010-200. Mốc biên tập: 23/10/2026. Bạn sẽ viết script Bash đọc một fixture ticket, kiểm tra tham số, chọn bản ghi OPEN và trả trạng thái khi đầu vào không hợp lệ. Lab chạy trong container Ubuntu 26.04.1 ARM64, Bash 5.3.9, với dữ liệu tự tạo; không thay đổi tài khoản hoặc dịch vụ production.
Script nhận đúng một tham số: đường dẫn đến tệp văn bản thường mà tài khoản hiện tại đọc được. Mỗi dòng fixture có ba trường mã, trạng thái, nhóm, phân cách bằng dấu |. Tệp có newline ở cuối mỗi bản ghi, không có dấu phân cách bên trong trường. Script không nhận trách nhiệm phân tích CSV tùy ý, JSON hoặc dữ liệu đa dòng.
Đầu ra stdout là danh sách mã ticket OPEN. stderr ghi số lượng để minh họa cách tách dữ liệu với thông tin tổng hợp. Mã 2 báo số tham số không đúng; mã 3 báo đầu vào không phải tệp thường đọc được. Những mã này là quy ước của script bài học, không phải ý nghĩa bắt buộc của mọi chương trình Linux.
Một bài học nhỏ nên nói cả điều chưa làm: script chưa kiểm tra đầy đủ schema, chưa xử lý tệp thay đổi đồng thời và chưa bảo đảm báo cáo nhất quán cho hệ thống đang ghi liên tục. Dùng fixture tĩnh để kiểm chứng hành vi trước khi thiết kế công cụ công việc. Không gọi script là một giải pháp production chỉ vì nó chạy đúng bốn dòng mẫu.
Giữ cùng phiên Bash cho các bước. Khối dưới tạo thư mục tạm và tệp tickets.txt; không cần sudo. Dấu > ghi mới hoặc cắt ngắn tệp đích nếu tái sử dụng, vì vậy chỉ dùng trong đường dẫn mới do mktemp trả về. Không đổi đầu vào thành log có thông tin cá nhân để thử.
lpi_script=$(mktemp -d)
printf '%s\n' \
'T001|OPEN|printer' \
'T002|CLOSED|network' \
'T003|OPEN|network' \
'T004|OPEN|printer' > "$lpi_script/tickets.txt"
cat "$lpi_script/tickets.txt"
Bạn dự kiến thấy bốn dòng. Nếu phiên shell bị đóng, biến lpi_script sẽ không tự phục hồi. Ghi đường dẫn trong ghi chú lab thay vì đoán một thư mục tạm của lần chạy khác. Với dữ liệu công việc, nhật ký phải được loại bí mật trước khi chia sẻ.
Dòng đầu #!/bin/bash khai báo interpreter cho cách thực thi trực tiếp được hệ thống hỗ trợ. Khi dùng bash đường-dẫn-script, bạn đã chọn Bash từ câu lệnh gọi; file chưa cần có execute bit để Bash đọc theo cách đó. Khi gọi trực tiếp bằng đường dẫn, quyền thực thi, quyền đi qua thư mục, interpreter và tùy chọn mount đều có thể ảnh hưởng.
Không mặc định /bin/sh là Bash. Trên nhiều hệ, nó là một shell khác; cú pháp riêng của Bash có thể không chạy như bạn mong. Chọn rõ môi trường bài học, kiểm tra bash --version và không đổi symlink hệ thống để làm một script chạy được. Nếu cần script portable, đó là yêu cầu thiết kế riêng.
Một script có tên ở thư mục hiện tại cũng không chắc được tìm khi chỉ gõ tên. PATH quyết định nơi shell tìm lệnh theo tên trong các trường hợp phù hợp. Dùng đường dẫn rõ ràng trong lab; không thêm dấu . vào đầu PATH như cách sửa chung. Bash Reference Manual là nguồn đối chiếu cú pháp, tham số và cách gọi shell.

Trong khối dưới, delimiter SCRIPT được quote ở đầu here-document. Nhờ vậy các biến như $1 và $# được giữ trong nội dung tệp để Bash xử lý khi script chạy, không bị shell đang tạo tệp thay trước. Dấu > chỉ ghi vào report.sh của thư mục lab mới.
cat > "$lpi_script/report.sh" <<'SCRIPT'
#!/bin/bash
if [ "$#" -ne 1 ]; then
printf '%s\n' 'Usage: report.sh INPUT' >&2
exit 2
fi
input=$1
if [ ! -f "$input" ] || [ ! -r "$input" ]; then
printf '%s\n' 'Input must be a readable regular file.' >&2
exit 3
fi
count=0
while IFS='|' read -r ticket status category; do
if [ "$status" = OPEN ]; then
printf '%s\n' "$ticket"
count=$((count + 1))
fi
done < "$input"
printf 'open=%s\n' "$count" >&2
SCRIPT
Các khoảng trắng trong biểu thức [ ... ] là một phần cú pháp cần giữ. $# là số tham số, $1 là tham số đầu tiên. Quoting quanh "$input" giữ đường dẫn có khoảng trắng thành một đối số. Nó không xác minh tệp đáng tin hoặc loại mọi rủi ro khi chương trình khác diễn giải nội dung; bài chỉ đọc văn bản theo hợp đồng đã nêu.
if chọn nhánh theo trạng thái lệnh kiểm tra. -f hỏi tệp thường, -r hỏi khả năng đọc theo kiểm tra hiện tại. Tệp vẫn có thể đổi giữa lúc kiểm tra và đọc; đây không phải cơ chế chống race cho môi trường đối kháng. Không mở rộng ví dụ thành xử lý dữ liệu không tin cậy với quyền cao.
read -r tránh việc backslash bị dùng như escape theo cách đọc mặc định; IFS='|' xác định ký tự phân trường cho lệnh read đó. Tên biến ticket, status và category nhận các phần dữ liệu theo quy tắc read. Nếu dữ liệu sai cấu trúc, script hiện chưa báo lỗi schema đầy đủ. Vì thế fixture và điều kiện newline phải được giữ khi đánh giá kết quả.
Chuyển hướng done < "$input" đưa tệp vào stdin của vòng lặp. Nó tránh cách pipe vào while có thể tạo một subshell trong nhiều cấu hình Bash, khiến giá trị bộ đếm ngoài vòng lặp khác dự đoán. Không cần học mọi trường hợp subshell ngay; ở đây hãy nhận biết phạm vi thực thi có thể ảnh hưởng trạng thái biến.
Khi status bằng OPEN, script in mã ticket rồi tăng count bằng arithmetic expansion. printf dùng định dạng cố định; dữ liệu nằm ở đối số riêng. Không dùng nội dung ticket làm chuỗi định dạng hoặc ghép thành lệnh eval. Đây là cách giữ dữ liệu khỏi bị diễn giải thêm như cú pháp shell.

bash -n "$lpi_script/report.sh"
bash "$lpi_script/report.sh" "$lpi_script/tickets.txt" \
> "$lpi_script/open-ids.txt" 2> "$lpi_script/summary.txt"
cat "$lpi_script/open-ids.txt"
cat "$lpi_script/summary.txt"
bash -n kiểm tra cú pháp mà không thực hiện nội dung script theo cách chạy thông thường. Nó không chứng minh đường dẫn tồn tại, logic đúng hoặc dữ liệu hợp lệ. Sau khi chạy fixture, stdout dự kiến có T001, T003, T004; stderr có open=3. Đọc cả hai để không nhầm một tệp trống với kết quả không có yêu cầu mở.
Hai redirect ghi đè các tệp kết quả nếu chạy lại cùng đường dẫn. Giữ chúng trong lab và không đặt một tệp kết quả trùng đầu vào. Khi cần công cụ công việc, thiết kế cách tạo kết quả mới, kiểm tra rồi thay thế có phục hồi, thay vì coi mọi kết quả đã ghi là sẵn sàng gửi đi.
Chạy thiếu tham số là một ca kiểm thử, không phải lỗi bất ngờ. Thu mã trả về ngay trong nhánh else vì một lệnh khác sẽ thay $?. Không dùng echo trước rồi mới hỏi trạng thái của script. Trong ví dụ, thông báo được giữ ở tệp lab, không cần giấu bằng chuyển mọi stderr vào /dev/null.
if bash "$lpi_script/report.sh" 2> "$lpi_script/noargs.txt"; then
printf '%s\n' 'Không mong đợi: script chấp nhận thiếu tham số.'
else
lpi_rc=$?
printf 'status=%s\n' "$lpi_rc"
fi
Mã dự kiến là 2. Với một đường dẫn không tồn tại được truyền làm tham số, mã dự kiến là 3. Thử thêm một bản sao fixture có khoảng trắng trong tên để xác nhận quoting giữ đường dẫn. Các ca này kiểm tra hợp đồng đầu vào, chưa chứng minh toàn bộ lỗi filesystem hoặc thiếu dung lượng đã được xử lý.
Một vòng for có thể duyệt danh sách giá trị; quoting quyết định ranh giới từng mục. Trong ví dụ dưới, có đúng hai lần in, dù mỗi giá trị có khoảng trắng. Đừng dùng for trên kết quả ls để xử lý tên tệp tùy ý: khoảng trắng và ký tự đặc biệt có thể làm dữ liệu bị tách sai.
for label in 'first item' 'second item'; do
printf '<%s>\n' "$label"
done
chmod u+x "$lpi_script/report.sh"
"$lpi_script/report.sh" "$lpi_script/tickets.txt"
chmod thay quyền của đúng script lab, không thay đệ quy cả thư mục. Execute bit chưa đủ nếu filesystem bị mount noexec; container thử nghiệm của bài dùng vùng /tmp cho phép thực thi để kiểm tra cách gọi trực tiếp. Không gỡ chính sách noexec của máy tổ chức để vượt một bài tập; bạn có thể kiểm tra cách gọi interpreter trong môi trường được phép.
Vì sao cần quote delimiter của here-document? Để giữ biến trong nội dung script cho thời điểm chạy. bash -n đạt có chứng minh báo cáo đúng? Không; còn cần fixture và kiểm tra đầu ra. Script thiếu tham số trả 2 có nghĩa mọi lệnh Linux dùng 2 cho cùng lỗi? Không; đó là quy ước của script này.
Bạn đạt bài khi giải thích được ba mã trạng thái trong phạm vi ví dụ, tái tạo ba mã ticket OPEN và kiểm tra được đường dẫn có khoảng trắng. Giữ script cùng fixture và phiên bản môi trường để người khác tái hiện. Khi nhu cầu chuyển sang dữ liệu phức tạp, hãy xem lại định dạng, cơ chế lỗi và công cụ phù hợp trước khi kéo dài script nhập môn.

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.

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ế.