Câu 1while read line; do … done < file xử lý file như thế nào?
Đọc đầu vào — while read xử lý từng dòng
Bài viết này là một phần của Khóa học Linux, giúp bạn thành thạo từ đầu những kỹ năng Linux thực tiễn, từ các lệnh cơ bản đến lập trình shell script.
Dùng while read name age để tách data.txt theo từng dòng thành các cột, xử lý lần lượt nhiều tệp truyền qua tham số bằng for f in "$@", và đưa đầu vào cho read qua pipe hay here-doc — minh họa và thực hành trên terminal trong trình duyệt.
Đọc tệp từng dòng một — while read
Bạn thường muốn xử lý một danh sách tên hay tệp cấu hình theo từng dòng.
Khi bạn viết while read line; do … done < file, nó đọc file từ trên xuống mỗi lần một dòng, đưa dòng đó vào biến line rồi lặp lại phần nằm giữa do … done.
Dù tệp có bao nhiêu dòng đi nữa, nó vẫn tự động lặp đến dòng cuối cùng.
read tách dòng nhận được theo khoảng trắng và đưa các phần vào nhiều biến khác nhau.
Khi bạn viết while read name age; do …, từ đầu tiên của mỗi dòng đi vào name, từ tiếp theo đi vào age.
Với dữ liệu được sắp xếp theo dạng "tên giá trị" trong một danh sách tên, bạn lấy được từng cột ra và định dạng lại.
printf 'web running\ndb stopped\n' > services.txt # tạo tệp nguyên liệu
while read svc state; do # mỗi dòng vào svc và state
echo "Service $svc is $state"
done < services.txt # lấy services.txt làm đầu vào
# đầu ra: Service web is running / Service db is stopped
while read name age đọc tệp mỗi lần một dòng, tách theo khoảng trắng rồi đưa từ đầu vào name, từ tiếp theo vào age.| Cú pháp | Ý nghĩa | Ví dụ |
|---|---|---|
while read v; do … done < f | Xử lý tệp f từng dòng một bằng biến v | while read l; do echo "$l"; done < data.txt |
while read a b; do … | Tách một dòng theo khoảng trắng vào nhiều biến | while read name age; do echo "$name"; done < data.txt |
Nhận nhiều tệp qua tham số — "$@" và $(cat f)
Nếu bạn cố định tên tệp cần xử lý ngay trong script, bạn không dùng được nó cho tệp khác.
Khi bạn truyền tham số lúc chạy, ví dụ ./show.sh a.txt b.txt, thì $1 là tham số thứ nhất còn $2 là tham số thứ hai.
Bạn nhận được tất cả tham số cùng lúc bằng "$@".
Khi bạn viết for f in "$@"; do … done, từng tệp được truyền vào sẽ lần lượt vào biến f để bạn xử lý theo thứ tự.
Khi muốn dùng toàn bộ tệp như một chuỗi, hãy đưa nó vào biến bằng text=$(cat "$f").
Bạn biết được số lượng tham số qua $#, nên có thể cho script hiện cách dùng khi không có tham số nào.
printf 'line A1\nline A2\n' > a.txt # nguyên liệu 1
printf 'line B1\n' > b.txt # nguyên liệu 2
vi show.sh # mở bằng trình soạn thảo, viết nội dung sau rồi lưu bằng :wq
# #!/bin/sh
# for f in "$@"; do
# echo "--- $f ---"
# cat "$f"
# done
chmod +x show.sh
./show.sh a.txt b.txt # hiện lần lượt 2 tệp kèm tiêu đề
$1 $2, và với "$@" bạn nhận hết rồi xử lý từng cái bằng for.| Cú pháp | Ý nghĩa | Ví dụ |
|---|---|---|
for f in "$@"; do … | Xử lý lần lượt từng tệp tham số | for f in "$@"; do cat "$f"; done |
$(cat f) | Đưa toàn bộ tệp vào một chuỗi duy nhất | text=$(cat data.txt) |
$# | Số lượng tham số (nếu 0 thì hiện cách dùng) | [ "$#" -eq 0 ] && echo usage |
Đầu vào tương tác và đầu vào qua pipe — read và here-doc
read đọc được không chỉ tệp mà cả đầu vào do người dùng gõ vào hay đầu vào truyền qua pipe.
Có các dạng như nối bằng pipe kiểu echo hi | read v, hoặc đưa cả cụm đầu vào tại chỗ bằng here-doc <<'EOF' … EOF.
Đây là nền tảng khi bạn kết hợp script với các lệnh khác.
# đọc đầu ra của pipe từng dòng một
printf 'one\ntwo\n' | while read v; do echo "line: $v"; done
# đưa đầu vào tại chỗ bằng here-doc
while read v; do echo "value: $v"; done <<'EOF'
x
y
EOF
# nhập tay đúng một lần (gõ name rồi Enter)
# chạy dòng này thì nó sẽ chờ bạn nhập
read name; echo "hello, $name"
while read từng dòng một bằng pipe hoặc here-doc.| Cú pháp | Ý nghĩa | Ví dụ |
|---|---|---|
cmd | while read v; do … | Xử lý đầu ra của pipe từng dòng một | cat data.txt | while read l; do echo "$l"; done |
<<'EOF' … EOF | Đưa đầu vào tại chỗ bằng here-doc | while read l; do echo "$l"; done <<'EOF' |
Kiểm tra kiến thức
Hãy trả lời từng câu hỏi một.
Câu 2Cách viết nào đại diện cho toàn bộ tham số đã truyền cho script?
Câu 3Làm sao để xử lý đầu ra của một lệnh từng dòng một bằng read mà không tạo tệp?