Mọi pattern dưới đây đã test bằng Python re và GNU grep 3.7. Lưu ý: grep -E (ERE) không hiểu \d, (?:...), lookahead, nên viết [0-9] và (...). Cần \d, \K, lookaround thì dùng grep -P. Muốn kiểm tra IP hợp lệ thì dùng module ipaddress thay vì regex.
Cú pháp cơ bản
| Ký hiệu | Nghĩa | Có trong grep -E? |
|---|---|---|
. * + ? {m,n} |
Ký tự bất kỳ, lặp 0+, 1+, 0-1, m đến n lần | có |
^ $ |
Đầu, cuối dòng | có |
[0-9a-f] [^ ] |
Lớp ký tự, phủ định | có |
(a|b) |
Nhóm, hoặc | có |
\b |
Ranh giới từ | có (GNU) |
[[:space:]] [[:digit:]] |
Lớp POSIX | có |
\d \s \w |
Số, khoảng trắng, chữ/số/_ | không, dùng -P |
(?:...) |
Nhóm không bắt | không, dùng -P |
(?=...) (?<!...) |
Lookahead, lookbehind | không, dùng -P |
*? +? |
Lặp non-greedy | không, dùng -P |
Địa chỉ IP, prefix
| Mục đích | Regex (Python / grep -P) |
|---|---|
IPv4 dạng lỏng (bắt cả 999.1.1.1) |
\b(?:\d{1,3}\.){3}\d{1,3}\b |
| IPv4 chặt (0-255 mỗi octet) | \b(?:(?:25[0-5]|2[0-4]\d|1?\d?\d)\.){3}(?:25[0-5]|2[0-4]\d|1?\d?\d)\b |
IPv4 prefix /0-/32 |
\b(?:\d{1,3}\.){3}\d{1,3}/(?:3[0-2]|[12]?\d)\b |
Ứng viên IPv6 (rồi kiểm bằng ipaddress) |
(?<![\w:])(?:[0-9A-Fa-f]{0,4}:){2,7}[0-9A-Fa-f]{0,4}(?![\w:]) |
| Mục đích | Lệnh grep -E |
|---|---|
| Lấy mọi IPv4 trong log | grep -Eo '\b([0-9]{1,3}\.){3}[0-9]{1,3}\b' app.log |
| Top IP xuất hiện nhiều nhất | grep -Eo '\b([0-9]{1,3}\.){3}[0-9]{1,3}\b' app.log | sort | uniq -c | sort -rn | head |
| Lấy prefix có mask | grep -Eo '\b([0-9]{1,3}\.){3}[0-9]{1,3}/[0-9]{1,2}\b' config.txt |
| Che IP trước khi gửi log cho người khác | sed -E 's/\b([0-9]{1,3}\.){3}[0-9]{1,3}\b/x.x.x.x/g' app.log |
MAC address
| Mục đích | Regex |
|---|---|
Dạng 00:1a:2b:3c:4d:5e hoặc 00-1A-... |
\b(?:[0-9A-Fa-f]{2}[:-]){5}[0-9A-Fa-f]{2}\b |
Dạng Cisco 001a.2b3c.4d5e |
\b(?:[0-9A-Fa-f]{4}\.){2}[0-9A-Fa-f]{4}\b |
grep -E, không phân biệt hoa thường |
grep -Eio '\b([0-9a-f]{2}[:-]){5}[0-9a-f]{2}\b' arp.txt |
grep -E dạng Cisco |
grep -Eio '\b([0-9a-f]{4}\.){2}[0-9a-f]{4}\b' mac-table.txt |
Interface
| Mục đích | Regex |
|---|---|
| IOS XR (Gi/TenGigE/HundredGigE/Bundle-Ether/Loopback, có sub-if) | \b(?:(?:Gigabit|TenGig|TwentyFiveGig|FortyGig|HundredGig|FourHundredGig)E(?:thernet)?|Bundle-Ether|Loopback)\d+(?:/\d+)*(?:\.\d+)?\b |
Junos (ge-0/0/0.100, et-0/0/0:1, ae0.0, lo0, irb.100) |
\b(?:(?:ge|xe|et|mge)-\d+/\d+/\d+(?::\d+)?|ae\d+|lo0|irb)(?:\.\d+)?\b |
grep -E nhanh cho IOS XR |
grep -Eo '(TenGigE|HundredGigE|GigabitEthernet|Bundle-Ether)[0-9/.]+' run.txt |
Log, timestamp, sự kiện
| Mục đích | Regex / lệnh |
|---|---|
Timestamp syslog Nov 6 08:01:02 |
^[A-Z][a-z]{2} [ \d]\d \d{2}:\d{2}:\d{2} |
| Timestamp ISO 8601 | \d{4}-\d{2}-\d{2}[T ]\d{2}:\d{2}:\d{2} |
Neighbor BGP đổi trạng thái (IOS XR ADJCHANGE) |
neighbor (\S+) (Up|Down) |
| Chỉ lấy IP neighbor bị Down | grep -Po 'neighbor \K\S+(?= Down)' syslog |
| Đếm số lần flap | grep -Ec 'ADJCHANGE.*Down' syslog |
Tách cặp key=value (value có thể trong ngoặc kép) |
(\w+)=("[^"]*"|\S+) |
Version dạng v7.11.2 |
\bv?(\d+)\.(\d+)\.(\d+)\b |
Config và giá trị
| Mục đích | Regex / lệnh |
|---|---|
Bỏ dòng trống và comment (#, !) |
grep -Ev '^[[:space:]]*(#|!|$)' config.txt |
| VLAN ID hợp lệ 1-4094 (cả chuỗi) | ^(?:409[0-4]|40[0-8]\d|[1-3]\d{3}|[1-9]\d{0,2})$ |
| ASN 16-bit private 64512-65534 (cả chuỗi) | ^(?:6451[2-9]|645[2-9]\d|64[6-9]\d\d|65[0-4]\d\d|655[0-2]\d|6553[0-4])$ |
| Xem khối config quanh một dòng | grep -E -B2 -A10 '^interface Bundle-Ether100' run.txt |
Python re
import ipaddress
import re
IPV4 = re.compile(r"\b(?:\d{1,3}\.){3}\d{1,3}\b")
BGP_ADJ = re.compile(r"neighbor (?P<peer>\S+) (?P<state>Up|Down)")
def is_ip(s: str) -> bool:
try:
ipaddress.ip_address(s)
return True
except ValueError:
return False
line = "%ROUTING-BGP-5-ADJCHANGE : neighbor 198.51.100.1 Down - Hold timer expired, peer 999.1.1.1"
m = BGP_ADJ.search(line)
if m:
print(m["peer"], m["state"]) # 198.51.100.1 Down
# regex lỏng để tìm, ipaddress để kiểm tra
print([s for s in IPV4.findall(line) if is_ip(s)]) # ['198.51.100.1']| Hàm | Dùng khi |
|---|---|
re.search(p, s) |
Tìm match đầu tiên ở bất kỳ đâu |
re.match(p, s) / re.fullmatch(p, s) |
Match từ đầu chuỗi / toàn bộ chuỗi |
re.findall(p, s) |
Lấy tất cả (trả tuple nếu có nhiều nhóm) |
re.finditer(p, s) |
Duyệt từng match, lấy được m.start(), m["name"] |
re.sub(p, repl, s) |
Thay thế, repl dùng \1 hoặc \g<name> |
re.compile(p, re.I | re.M) |
Biên dịch một lần, cờ không phân biệt hoa thường / đa dòng |
Luôn viết pattern Python dạng raw string r"..." để \b, \d không bị Python hiểu thành escape. Trong shell, bọc pattern bằng nháy đơn '...'.