Học bằng cách đọc theo thứ tự

Kết hợp lệnh — Rẽ nhánh theo kết quả grep / awk

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.
Trong script, đưa số đếm vào biến bằng n=$(grep -c ERROR access.log), lấy tổng bằng total=$(awk '{s+=$1} END{print s}'), rồi rẽ nhánh bằng if [ "$n" -ge 2 ] — một script quét log được minh họa và viết ngay trên terminal trong trình duyệt.

Gọi lệnh bên trong script — pipe và thay thế lệnh

Sức mạnh thật sự của shell script nằm ở chỗ bạn kết hợp được các lệnh thông thường như `grep` `awk` `sed` `sort` `wc` ngay bên trong nó.

Ngay trong script, bạn cũng nối các lệnh bằng pipe | như khi gõ ở dấu nhắc, và lấy đầu ra của chúng vào biến bằng thay thế lệnh $(cmd).

Viết n=$(grep -c error log.txt) thì số dòng khớp mà grep -c đếm được sẽ vào biến n.

Bạn cũng đưa được một kết quả tổng hợp vào biến, như total=$(awk '{s+=$1} END{print s}' f).

Khi kết quả của một lệnh đã nằm trong biến, bạn dùng giá trị đó để thay đổi cách script hoạt động.

printf 'INFO ok\nERROR disk\nINFO ok\nERROR cpu\n' > sys.log   # tạo tệp dữ liệu mẫu
vi count.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
#   n=$(grep -c ERROR sys.log)
#   echo "ERROR count: $n"
chmod +x count.sh
./count.sh                                                      # ERROR count: 2
Đưa số đếm vào biến rồi rẽ nhánh
n=$(grep -c ERROR access.log)n ≥ 2in ra cảnh báon < 2in ra OKtừ 2 trở lêndưới 2cảnh báobình thường
Số dòng mà grep -c đếm được đi vào biến n, còn if rẽ sang cảnh báo khi nó từ 2 trở lên và in OK trong các trường hợp còn lại.
Cú phápÝ nghĩaVí dụ
n=$(grep -c pat f)Đưa số dòng khớp vào biến nn=$(grep -c error log.txt)
$(cmd | cmd)Lấy kết quả của một pipeline vào biếntop=$(sort log.txt | head -1)
if [ "$n" -ge 2 ]; then …Rẽ nhánh tùy theo số đếm có từ 2 trở lên hay khôngif [ "$n" -ge 2 ]; then echo warn; fi

Viết một script quét log đếm số dòng lỗi trong một tệp kiểu access log bằng grep -c, rồi in cảnh báo khi có từ 2 dòng trở lên và in OK trong trường hợp còn lại.

① Chạy printf 'INFO login\nERROR timeout\nINFO logout\nERROR 500\nINFO ping\n' > access.log để tạo tệp dữ liệu mẫu.

② Mở scan.sh bằng vi scan.sh rồi nhấn i để vào insert mode. Đưa số đếm vào biến bằng n=$(grep -c ERROR access.log), rồi in ra "WARN: $n errors found" hoặc "OK" bằng if [ "$n" -ge 2 ]; then … else … fi (đặt #!/bin/sh ở dòng đầu). Viết xong thì nhấn Esc:wq để lưu.

③ Thêm quyền thực thi bằng chmod +x scan.sh, chạy ./scan.sh, và xác nhận cảnh báo hiện ra.

④ Nếu bạn chưa chắc nên viết gì, hãy sao chép nội dung trong bảng đáp án rồi dán vào insert mode của vi.

(Chạy đúng thì phần giải thích sẽ hiện ra.)

Linux console
0 / 3 đã hoàn thành
Loading Linux Terminal...

Tính tổng rồi hành động theo kết quả — awk và rẽ nhánh

Ngoài số đếm, bạn còn cộng được các con số lại rồi quyết định dựa trên tổng.

total=$(awk '{s+=$1} END{print s}' f) cộng cột 1 của mọi dòng rồi đưa tổng vào biến total.

Khi bạn muốn biết số lần xuất hiện của từng từ, hãy dùng sort f | uniq -c: uniq chỉ gộp được các dòng trùng nằm liền nhau, nên phải sort trước rồi mới đếm, sau đó lấy kết quả đó vào biến bằng $(...).

So tổng với một ngưỡng, như if [ "$total" -gt 100 ]; then …, thì bạn ra được quyết định kiểu "cảnh báo khi tổng vượt quá một mức".

Gộp phần tính tổng và rẽ nhánh vào cùng một script và bạn có một công cụ thiết thực đọc dữ liệu rồi xác định tình hình đang ra sao.

printf '40\n30\n50\n' > sales.txt   # dữ liệu số 1 cột
vi sum.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
#   total=$(awk '{s+=$1} END{print s}' sales.txt)
#   if [ "$total" -gt 100 ]; then
#     echo "total $total: goal met"
#   else
#     echo "total $total: not met"
#   fi
chmod +x sum.sh
./sum.sh                              # total 120: goal met
printf 'a\na\nb\n' > tags.txt   # tệp dữ liệu mẫu có các từ lặp lại
sort tags.txt | uniq -c          # sắp xếp trước rồi đếm
# đầu ra:
#   2 a
#   1 b
Cộng tổng rồi rẽ nhánh theo giá trị của nó
total=$(awk … errors.txt)total > 20cần xử lýtotal ≤ 20trong giới hạnvượttrong mứcvượt ngưỡngtrong ngưỡng
if so totalawk cộng được với ngưỡng 20, rẽ sang needs action khi vượt và within limit khi không vượt.
Cú phápÝ nghĩaVí dụ
awk '{s+=$1} END{print s}' fCộng cột 1awk '{s+=$1} END{print s}' nums.txt
sort f | uniq -cSắp xếp trước để các dòng trùng nằm liền nhau rồi đếmsort tags.txt | uniq -c
if [ "$total" -gt N ]; then …Rẽ nhánh bằng cách so tổng với ngưỡng Nif [ "$total" -gt 100 ]; then …

Viết một script cộng dữ liệu số bằng awk và đổi kết quả tùy theo nó có vượt ngưỡng hay không.

① Chạy printf '12\n8\n5\n3\n' > errors.txt để tạo dữ liệu số 1 cột.

② Mở sum.sh bằng vi sum.sh rồi nhấn i để vào insert mode. Đưa tổng vào biến bằng total=$(awk '{s+=$1} END{print s}' errors.txt), rồi in ra "needs action: total $total" hoặc "within limit: total $total" bằng if [ "$total" -gt 20 ]; then … else … fi (đặt #!/bin/sh ở dòng đầu). Viết xong thì nhấn Esc:wq để lưu.

③ Thêm quyền thực thi bằng chmod +x sum.sh, chạy ./sum.sh, và xác nhận bạn nhận được kết quả đã rẽ nhánh theo tổng.

④ Nếu bạn chưa chắc nên viết gì, hãy sao chép nội dung trong bảng đáp án rồi dán vào insert mode của vi.

Linux console
0 / 3 đã hoàn thành
Loading Linux Terminal...

Điều hướng đầu ra và lỗi — > và 2>

Đôi khi bạn muốn giữ kết quả của script trong một tệp thay vì để trên màn hình.

./scan.sh > report.txt ghi đầu ra thông thường (đầu ra chuẩn) vào report.txt.

Đưa thông báo lỗi sang lỗi chuẩn bằng echo "..." 1>&2, rồi ./scan.sh > report.txt 2> error.log cho phép bạn giữ kết quả bình thường và lỗi trong hai tệp riêng.

vi job.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
#   echo "processing started"          # đầu ra chuẩn
#   echo "note: input was empty" 1>&2     # lỗi chuẩn
chmod +x job.sh
./job.sh > out.txt 2> err.txt        # đưa đầu ra và lỗi sang hai tệp riêng
cat out.txt                          # processing started
cat err.txt                          # note: input was empty
Điều hướng đầu ra và lỗi
./scan.shecho(đầu ra chuẩn)> report.txt./scan.shecho … 1>&2(lỗi chuẩn)2> error.logđầu ra chuẩnlỗi chuẩn
Đầu ra chuẩn đi vào > report.txt còn lỗi chuẩn đi vào 2> error.log, nên hai thứ nằm ở hai tệp riêng.
Cú phápÝ nghĩaVí dụ
cmd > outGhi đầu ra chuẩn vào một tệp./scan.sh > report.txt
echo "…" 1>&2Đưa một thông báo sang lỗi chuẩnecho "failed" 1>&2
cmd > out 2> errĐiều hướng đầu ra và lỗi sang hai tệp riêng./scan.sh > report.txt 2> error.log

Viết một script điều hướng một thông báo bình thường và một thông báo lỗi sang hai tệp riêng.

① Mở job.sh bằng vi job.sh rồi nhấn i để vào insert mode. Đưa echo "done" sang đầu ra chuẩn và echo "warning" 1>&2 sang lỗi chuẩn (đặt #!/bin/sh ở dòng đầu). Viết xong thì nhấn Esc:wq để lưu.

② Thêm quyền thực thi bằng chmod +x job.sh, rồi tách đầu ra và lỗi sang hai tệp riêng bằng ./job.sh > out.txt 2> err.txt.

③ Chạy cat out.txtcat err.txt, và xác nhận thông báo bình thường và thông báo lỗi mỗi cái nằm trong tệp của riêng mình.

④ Nếu bạn chưa chắc nên viết gì, hãy sao chép nội dung trong bảng đáp án rồi dán vào insert mode của vi.

Linux console
0 / 3 đã hoàn thành
Loading Linux Terminal...
QUIZ

Kiểm tra kiến thức

Hãy trả lời từng câu hỏi một.

Câu 1n=$(grep -c error log.txt) đưa gì vào biến n?

Câu 2if [ "$n" -ge 2 ]; then … làm gì?

Câu 3Trong ./scan.sh > report.txt 2> error.log, 2> điều hướng cái gì?