Làm chủ Sed và Awk: Xử lý văn bản hiệu năng cao trên Linux

Linux tutorial - IT technology blog
Linux tutorial - IT technology blog

Cơn ác mộng của file log 5GB

Tôi đã từng chứng kiến một máy chủ Nginx thực tế (production) bị đình trệ hoàn toàn vì một file access log nặng 5GB. Máy chủ liên tục báo lỗi 502, và tôi cần xác định nguyên nhân ngay lập tức. Mục tiêu của tôi rất đơn giản: trích xuất mọi địa chỉ IP liên quan đến mã trạng thái 502 và đếm số lần xuất hiện của chúng để xác định một cuộc tấn công DDoS tiềm ẩn.

Các công cụ thông thường đã thất bại. Việc mở file bằng một trình soạn thảo văn bản truyền thống đã làm treo terminal của tôi. Trong khi grep có thể tìm thấy các dòng văn bản, nó lại không thể tổng hợp dữ liệu thành một báo cáo hữu dụng. Đây là bức tường mà nhiều quản trị hệ thống (sysadmin) gặp phải. Khi khối lượng dữ liệu vượt quá dung lượng RAM hiện có, các công cụ GUI và các lệnh cơ bản trở thành gánh nặng thay vì là tài sản.

Tại sao trình soạn thảo yêu thích của bạn vừa bị treo

Vấn đề cốt lõi nằm ở việc quản lý bộ nhớ. Hầu hết các trình soạn thảo văn bản và ứng dụng bảng tính đều hoạt động dựa trên cơ chế “buffer-based” (dựa trên bộ đệm), nghĩa là chúng cố gắng tải toàn bộ tệp vào RAM trước khi bạn có thể nhìn thấy dòng đầu tiên. Nếu bạn cố gắng mở một file log 5GB trên một máy chủ chỉ có 4GB bộ nhớ, kernel sẽ bắt đầu sử dụng bộ nhớ ảo (swapping) vào đĩa cứng, cuối cùng làm đóng băng hệ thống.

Ngay cả một script Python cũng có thể ngốn bộ nhớ nếu bạn sử dụng .readlines(), lệnh này lưu trữ mọi dòng dưới dạng một đối tượng chuỗi trong một danh sách. Để xử lý các tập dữ liệu khổng lồ, bạn cần các công cụ sử dụng xử lý luồng (stream processing). Sed và Awk đọc dữ liệu theo từng dòng một. Chúng thực hiện một hành động, loại bỏ dòng đó và chuyển sang dòng tiếp theo. Điều này giữ cho mức sử dụng bộ nhớ cực kỳ thấp — thường dưới 15MB — bất kể tệp đó nặng 10MB hay 100GB.

Lựa chọn công cụ: Sed vs. Awk vs. Python

Tôi thường chọn một trong ba công cụ tùy thuộc vào độ phức tạp của tác vụ. Mỗi công cụ đều có một vị trí cụ thể trong quy trình làm việc DevOps.

  • Python: Dùng cho logic phức tạp, gọi API hoặc khi cần xuất dữ liệu JSON. Nhược điểm? Nó khá dài dòng cho các tác vụ đơn giản và có thời gian khởi động chậm hơn so với các tiện ích nhị phân.
  • Sed (Stream Editor): Đây là công cụ chính để chuyển đổi văn bản. Nếu bạn cần thay thế chuỗi, xóa các dòng cụ thể hoặc loại bỏ khoảng trắng, Sed là vô đối. Nó dựa trên các biểu thức chính quy (regular expressions) tinh gọn để sửa đổi văn bản với tốc độ thường vượt quá 100MB mỗi giây.
  • Awk: Đây không chỉ là một lệnh; nó là một ngôn ngữ lập trình chuyên dụng để trích xuất dữ liệu. Awk coi các dòng là các bản ghi (records) và các từ là các trường (fields). Nếu dữ liệu của bạn trông giống như một bảng, Awk là lựa chọn đúng đắn.

Trên một instance Ubuntu 22.04 với tài nguyên hạn chế, các tiện ích này là cứu cánh. Tôi đã thấy Sed và Awk xử lý một file 5GB trong khoảng 90 giây trong khi vẫn giữ mức sử dụng RAM dưới 50MB. Một script Python tương đương có thể mất thời gian gấp đôi và yêu cầu xử lý bộ nhớ cẩn thận hơn.

Quy trình làm việc hai bước thực tế

Hiệu quả đến từ việc sử dụng Sed để “làm sạch” và Awk để “phân tích”. Việc kết chuỗi chúng lại với nhau tạo ra một đường ống dẫn dữ liệu (data pipeline) tốc độ cao.

1. Làm sạch dữ liệu nhanh chóng với Sed

Sed sử dụng cú pháp s/search/replace/g để sửa đổi các luồng dữ liệu. Nó hoàn hảo cho các cập nhật hàng loạt mà nếu làm thủ công sẽ mất hàng giờ.

Giả sử bạn cần cập nhật địa chỉ IP của cơ sở dữ liệu trên 150 file cấu hình khác nhau. Bạn có thể thực hiện việc đó chỉ bằng một lệnh duy nhất:

sed -i 's/192.168.1.10/10.0.0.50/g' *.conf

Cờ -i chỉnh sửa các file trực tiếp (“in-place”). Để loại bỏ tất cả các dòng chú thích và dòng trống khỏi một script để có cái nhìn gọn gàng hơn, bạn có thể sử dụng:

sed -e '/^#/d' -e '/^$/d' script.sh

Lệnh này tìm các dòng bắt đầu bằng # hoặc các dòng trống và xóa (d) chúng khỏi luồng xuất ngay lập tức.

2. Trích xuất dữ liệu với Awk

Awk coi mỗi dòng là một chuỗi các cột. Theo mặc định, nó sử dụng khoảng trắng làm dấu phân cách, gán từ đầu tiên cho $1, từ thứ hai cho $2, và cứ tiếp tục như vậy.

Hãy xem một mục log Nginx tiêu chuẩn:

172.16.0.5 - - [12/Nov/2023:10:00:01] "GET /api/v1/users HTTP/1.1" 404 512

Trong chuỗi này, địa chỉ IP là $1 và mã trạng thái là $9. Để tìm mọi IP đã gây ra lỗi 404, hãy chạy:

awk '$9 == 404 { print $1 }' access.log

Để có được con số thực tế về những kẻ tấn công duy nhất, chúng ta có thể sử dụng một mảng liên kết (associative array). Nó hoạt động giống như một dictionary trong Python nhưng nhanh hơn nhiều đối với các lệnh một dòng (one-liner) trên CLI:

awk '$9 == 404 { count[$1]++ } END { for (ip in count) print count[ip], ip }' access.log | sort -rn

Lệnh này xây dựng một bảng tần suất trong bộ nhớ và in kết quả cuối cùng sau khi file đã được xử lý xong. Đây là một công cụ báo cáo cấp độ chuyên nghiệp chỉ gói gọn trong một dòng mã.

3. Xây dựng một đường ống tự động hóa

Những công cụ này trở nên thực sự mạnh mẽ khi bạn kết nối chúng lại với nhau qua đường ống (pipe). Hãy tưởng tượng bạn có một file CSV lộn xộn với các chuỗi nằm trong dấu ngoặc kép và bạn cần tính tổng các giá trị ở cột thứ ba trong khi bỏ qua dòng tiêu đề.

# Loại bỏ dấu ngoặc kép, bỏ qua dòng đầu tiên và tính tổng cột 3
cat data.csv | sed 's/"//g' | awk -F',' 'NR > 1 { sum += $3 } END { print "Tổng cộng: ", sum }'

Tôi sử dụng chính xác mô hình này để tạo các bản tóm tắt tài chính hàng ngày từ các file log giao dịch thô. Nó nhanh hơn đáng kể so với việc nhập dữ liệu vào bảng tính và có thể được tự động hóa thông qua một tác vụ Cron đơn giản.

Lời kết

Đừng cảm thấy bạn cần phải ghi nhớ mọi tham số (flag). Hãy bắt đầu bằng cách sử dụng Sed cho các tác vụ tìm kiếm và thay thế cơ bản. Chuyển sang Awk khi bạn cần trích xuất các cột cụ thể từ file log hoặc thực hiện các phép toán cơ bản. Một khi bạn đã làm chủ được hai công cụ này, bạn sẽ hiếm khi cần phải rời khỏi terminal để xử lý dữ liệu. Quy trình làm việc này giúp máy chủ của bạn ổn định, các script chạy nhanh và mức sử dụng bộ nhớ luôn nằm trong tầm kiểm soát.

Share: