Sau mười một phần đào vào lý thuyết và cơ chế, bài cuối này mang regex về đúng nơi nó tỏa sáng nhất trong công việc hằng ngày: dòng lệnh, đặc biệt là phân tích log. Khi một service gặp sự cố lúc 2 giờ sáng, thứ cứu bạn không phải một framework hoành tráng mà là vài dòng grep -P gõ thẳng vào terminal để tìm ra IP nào đang spam, endpoint nào trả 500, request nào chậm. Bài này (phần 12, bài cuối loạt Regex) chạy thật một phiên điều tra log từ đầu tới cuối, so grep với ripgrep, và — quan trọng không kém — chỉ rõ khi nào nên bỏ regex xuống.

Bộ công cụ regex trên dòng lệnh

  • grep -P: grep với engine PCRE (Perl-compatible), có \d, \b, lookaround... -o chỉ in phần khớp, -c đếm dòng, \K "vứt" phần đã khớp trước đó.
  • ripgrep (rg): grep hiện đại, mặc định đệ quy, đa luồng, tự bỏ qua .git/binary — nhanh hơn hẳn trên cây thư mục lớn. -P bật PCRE2.
  • sed/awk: biến đổi và trích field; nhiều việc "tưởng cần regex" thực ra awk '{print $N}' gọn hơn.

Kết hợp chúng với pipe Unix (sort | uniq -c | sort -rn) là công thức xếp hạng kinh điển.

Ảnh chụp đoạn mã nền tối minh hoạ regex thực chiến phân tích log bằng grep -P ripgrep awk khi nào nên dừng, trích thông tin bằng grep -oP chỉ in phần khớp IP đầu dòng grep -oP mũ d 1 3 chấm d 1 3 lặp 3 access log status code backslash K vứt phần trước chỉ giữ d 3 grep -oP nháy kép backslash K d 3, đếm và xếp hạng regex cộng pipe Unix grep -cP 5 d 2 d cộng đếm dòng lỗi 5xx grep -oP status sort uniq -c sort -rn TOP status code grep -oP IP sort uniq -c sort -rn head 3 TOP IP gọi nhiều, ripgrep rg nhanh trên cây thư mục lớn rg -c b 5-9 d 2 ms cuối dòng request chậm hơn 500ms rg -P POST bị lỗi PCRE2 rg -P 500 logtree quét cả thư mục đa luồng, awk trích field theo vị trí không phải mọi việc cần regex awk 200 ms bằng trường cuối gsub bỏ ms cộng dồn tính trung bình cột cố định cắt field bằng trường cuối nhanh hơn regex phức tạp, khi nào dừng dùng regex không dùng regex parse HTML JSON lồng bài 02 05 không đếm được độ sâu định dạng có parser chuẩn JSON CSV HTML dùng parser đừng regex regex hợp nhất cho log dòng phẳng trích pattern tìm thay thế văn bản

Hình 1: Bộ công cụ regex dòng lệnh — grep -oP trích (với \K), pipe sort|uniq -c|sort -rn xếp hạng, ripgrep quét cây thư mục đa luồng, awk cắt field theo vị trí; và nguyên tắc dừng: không dùng regex parse HTML/JSON lồng.

Đo thật: một phiên điều tra log

Mình tạo một access.log 500 dòng (định dạng Apache/nginx), rồi điều tra:

Ảnh chụp bảng kết quả chạy thật phân tích log output thật go-lab access log grep -P ripgrep awk, a b trích và xếp hạng TOP status TOP IP grep -cP 5 d 2 bằng 32 dòng lỗi 5xx TOP status code grep -oP sort uniq -c sort -rn 359 200 75 404 32 500 23 301 11 403 tổng 359 cộng 75 cộng 32 cộng 23 cộng 11 bằng 500 bằng số dòng log khớp TOP 3 IP gọi nhiều nhất 52 10.0.0.3 46 10.0.0.6 43 10.0.0.7, c ripgrep lọc request chậm cộng POST lỗi output thật rg -c b 5-9 d 2 ms cuối dòng 181 request lớn hơn 500ms rg -P POST bị lỗi 192.168.1.4 POST api posts HTTP 404 1319 122ms 10.0.0.1 POST favicon ico HTTP 404 3107 271ms 192.168.1.2 POST login HTTP 404 4252 357ms, d awk trích field và ripgrep vs grep trên 40MB awk 200 200 OK 359 request latency trung bình 405.3 ms quét 500 trên 500000 dòng 200 file 40MB min 3 lần grep -rP 24.8ms 32000 dòng khớp rg -P 11.9ms 32000 dòng khớp ripgrep khoảng 2 lần nhanh hơn

Hình 2: Chạy thật — grep -cP ' 5\d{2} ' cho 32 dòng lỗi 5xx; TOP status 359 200, 75 404, 32 500, 23 301, 11 403 (tổng đúng 500); TOP IP 10.0.0.3(52)...; rg tìm 181 request >500ms và các POST lỗi; awk tính latency 200 OK trung bình 405.3ms; và quét 40MB: grep -rP 24.8ms vs rg -P 11.9ms.

  • (a)(b) Trích rồi xếp hạng — công thức vàng: grep -oP '" \K\d{3}' trích riêng status code (dùng \K để chỉ giữ ba chữ số sau dấu "), pipe qua sort | uniq -c | sort -rn cho bảng xếp hạng: 359 lần 200, 75 lần 404, 32 lần 500, 23 lần 301, 11 lần 403. Tổng đúng 500 — khớp số dòng log, một cách kiểm tra nhanh rằng regex không sót. Cùng công thức đổi mẫu thành IP cho TOP IP gọi nhiều nhất (10.0.0.3 với 52 lần). grep -cP ' 5\d{2} ' đếm nhanh 32 dòng lỗi server.
  • (c) ripgrep cho truy vấn phức tạp: rg -c '\b[5-9]\d{2}ms$' đếm 181 request chậm hơn 500ms (khớp phần ms cuối dòng). rg -P '"POST [^\"]+" [45]\d{2} ' (dùng lớp phủ định [^"] như bài 2 và 11 khuyên) lọc đúng các POST bị 4xx/5xx — thấy ngay POST /login trả 404. Đây là kiểu truy vấn "tìm giao của điều kiện" mà regex làm gọn hơn nhiều lần lọc thủ công.
  • (d) awk khi dữ liệu có cột, và ripgrep khi cây lớn: latency nằm ở field cuối mỗi dòng, nên awk '{ms=$NF...}' cắt nó ra gọn hơn viết regex phức tạp — tính được latency trung bình của request 200 là 405.3ms. Và khi quét cả cây log 40MB (500.000 dòng, 200 file), ripgrep mất 11.9ms còn grep -rP mất 24.8ms — ripgrep nhanh khoảng gấp đôi (cùng tìm ra 32000 dòng), nhờ đa luồng và tối ưu tìm kiếm.

Khi nào nên DỪNG dùng regex

Đây là bài học đúc kết từ cả loạt, quan trọng ngang mọi kỹ thuật đã học. Regex không phải búa vạn năng:

  • Đừng parse HTML/JSON/XML lồng nhau bằng regex (phần 2, 5): các định dạng này có cấu trúc lồng độ sâu tùy ý, mà regex thuần không đếm được độ sâu — đó là giới hạn lý thuyết, không phải chuyện viết khéo. Dùng parser chuẩn (thư viện JSON, jsoup cho HTML, jq cho JSON trên dòng lệnh).
  • Dữ liệu có định dạng chuẩn thì dùng công cụ chuẩn: CSV có thư viện CSV (xử lý đúng dấu phẩy trong ngoặc kép), log có định dạng cố định thì awk theo field an toàn hơn regex đoán mò.
  • Regex hợp nhất cho: log dòng-phẳng, trích pattern (email, IP, ngày), tìm/thay thế văn bản, kiểm định dạng đầu vào đơn giản. Đúng việc thì nó là công cụ mạnh nhất trong hộp đồ nghề.

Tổng kết loạt "Regex sâu" (12 phần)

Loạt bài đã đi từ bên trong cỗ máy ra tới thực chiến dòng lệnh:

  • Phần 1-4 — nền tảng: regex là máy trạng thái; tham lam vs lười; neo và ranh giới (zero-width); lớp ký tự và quantifier (và bẫy \w với chữ có dấu).
  • Phần 5-7 — lý thuyết và hiệu năng: backreference khiến regex "không còn regular"; catastrophic backtracking và ReDoS (một mẫu treo server); RE2 chạy tuyến tính, miễn nhiễm ReDoS nhưng bỏ vài tính năng.
  • Phần 8-10 — sức mạnh biểu đạt: lookahead/lookbehind (nhìn mà không nuốt); Unicode và tiếng Việt (\w, byte vs rune, NFC/NFD, \p{L}); nhóm bắt và thay thế.
  • Phần 11-12 — làm chủ: tối ưu (possessive/atomic biến 2 giây thành micro-giây); và thực chiến với grep/ripgrep/awk.

Sợi chỉ xuyên suốt: regex không phải phép thuật, mà là một cỗ máy có luật rõ ràng — hiểu luật đó thì bạn viết được mẫu vừa đúng, vừa nhanh, vừa an toàn, và biết khi nào nên dùng công cụ khác. Cảm ơn bạn đã theo trọn loạt bài. Chúc bạn khớp đúng mọi thứ cần khớp, và không bao giờ dính một con ReDoS nào.

Ba ý mang về

  1. grep -P + pipe Unix là bộ đồ nghề phân tích log: đo thật grep -oP '" \K\d{3}' | sort | uniq -c | sort -rn cho TOP status (359 lần 200, 75 lần 404...), tổng đúng 500 dòng; grep -cP đếm lỗi 5xx (32 dòng) — trích rồi xếp hạng là công thức vàng.
  2. ripgrep nhanh hơn cho cây lớn, awk gọn hơn cho field cố định: đo thật rg quét 40MB mất 11.9ms vs grep -rP 24.8ms (~2x); và awk '{ms=$NF}' tính latency 200 OK trung bình 405.3ms gọn hơn regex phức tạp — chọn công cụ theo hình dạng dữ liệu.
  3. Biết khi nào dừng dùng regex: đừng parse HTML/JSON lồng (giới hạn lý thuyết — phần 2, 5), dùng parser chuẩn; regex hợp nhất cho log dòng-phẳng, trích pattern, tìm/thay thế — đúng việc thì nó là công cụ mạnh nhất, sai việc thì nó là nguồn bug.

Nguồn