New FAMILUG

The PyMiers

Saturday 31 October 2020

grep không hỗ trợ \d và 3 mode regex trong grep

grep là công cụ thiết yếu của sysadmin, tác dụng cơ bản là tìm một mẫu string trong 1 file text. 

Cái tên grep bắt nguồn từ câu lệnh g/re/p của chương trình ed

> Its name comes from the ed command g/re/p (globally search for a regular expression and print matching lines)

Biết thêm một chút regex sẽ tăng thêm sức mạnh. Ví dụ in ra shell của các user trên máy:

$ grep -o '/bin/.*sh' /etc/passwd

/bin/bash
/bin/sh

Photo by Percy Pham on Unsplash

Một "pattern" phổ biến là tìm số, nếu đã từng lập trình dùng regex, thì \d không còn lạ gì để tìm số - code Python ngay trên CLI:

$ echo '123ab45' | python3 -c '
> import re, sys
> print(re.findall("\d+", sys.stdin.read()))'
['123', '45']

Nhưng thật bất ngờ, grep trên Ubuntu 18.04 không hỗ trợ pattern này:

$ echo '123ab45' | grep -o '\d+'

# không in ra gì 

Và nếu như bạn "có vẻ" là dân chuyên, "dùng egrep đi", thì kết quả cũng vẫn vậy:

$ echo '123ab45' | grep -oE '\d+'

# nguyễn y vân - vẫn y nguyên


Có thể bạn đã biết, trên Linux thường có thêm 2 câu lệnh fgrep và egrep, nhưng thực ra chúng chỉ là grep -F và grep -E 

$ cat `which fgrep`
#!/bin/sh
exec grep -F "$@"

$ cat `which egrep`
#!/bin/sh
exec grep -E "$@"

Nếu thực sự muốn thấy kết quả, phải dùng Perl Regex -P:

$ echo '123ab45' | grep -oP '\d+'
123
45

grep không chỉ là một chương trình

grep là tên của 1 loại chương trình, nó có nhiều "bản" khác nhau và các bản sẽ có vài phần giống và nhiều phần khác nhau.

Gõ grep --version để xem mình đang dùng gì:

$ grep --version
grep (GNU grep) 3.1
Copyright (C) 2017 Free Software Foundation, Inc.

Đây là GNU grep, mặc định trên các hệ điều hành Linux based.

Tren OSX/MacOS hay trên BSD, sẽ sử dụng bản grep khác 

obsd# uname -a; grep --version
OpenBSD obsd.hvnbsd.com 6.8 GENERIC#97 amd64
grep version 0.9

Ba loại Regex mà grep hỗ trợ

khi gõ grep 'pattern' files, grep sẽ dùng Basic Regular Expression (BRE)

$ grep '/bin/.*sh$' /etc/passwd

Khi gõ egrep hay grep -E 'pattern' files, grep sẽ dùng Extended Regular Expression (ERE). BRE và ERE trong GNU grep  về tính năng nhìn chung là giống nhau. Điểm khác biệt duy nhất là trong BRE, các ký tự đặc biệt phải escape với backslash \, ví dụ: () -> \(\)

       grep  understands  three  different  versions  of  regular
       expression syntax: “basic”  (BRE),  “extended”  (ERE)  and
       “perl”  (PCRE).   In  GNU  grep  there is no difference in
       available  functionality  between   basic   and   extended
       syntaxes.    In   other   implementations,  basic  regular
       expressions are less powerful.

       Perl-compatible   regular   expressions   give  additional
       functionality, and are  documented  in  pcresyntax(3)  and
       pcrepattern(3),  but work only if PCRE is available in the
       system.

Perl regex vốn nổi tiếng là nhiều tính năng, có thể dùng với option -P nếu trên máy có cài lib PCRE:

$ ldd `which grep` | grep pcre
    libpcre.so.3 => /lib/x86_64-linux-gnu/libpcre.so.3 (0x00007f92565f4000)

\d là cái gì? 

Theo man 1 grep, bachslash (\) theo sau nó là 1 ký tự, thường là cách viết tắt (shortcut) cho các pattern regex thường dùng nào đó.Xem 

bảng đầy đủ tại https://en.wikipedia.org/wiki/Regular_expression#Character_classes

trong grep, chỉ có \b \B \< \> \w - KHÔNG CÓ \d


       The Backslash Character and Special Expressions
       The  symbols \< and \> respectively match the empty string
       at the beginning and end of a word.  The symbol \b matches
       the empty string at the edge of a word, and \B matches the
       empty string provided it's not at the edge of a word.  The
       symbol  \w  is  a  synonym  for  [_[:alnum:]]  and \W is a
       synonym for [^_[:alnum:]].
 

\d trong các chương trình khác có nghĩa là sẽ match 1 chữ số, trên grep phải viết rõ character class ra [0-9]

$ echo '123ab45' | grep -o '[0-9]\+'
123
45

Hay ERE không cần escape meta character +:

$ echo '123ab45' | grep -oE '[0-9]+'
123
45

Các character class định nghĩa sẵn theo man 7 regex:

       Within a bracket expression, the name of a character class enclosed in "[:" and ":]" stands for the list of  all  characters
       belonging to that class.  Standard character class names are:

              alnum   digit   punct
              alpha   graph   space
              blank   lower   upper
              cntrl   print   xdigit

Regex là một vấn đề phức tạp, biết một chút cũng được nhưng nhớ nắm rõ các phiên bản, và tránh lạm dụng:

Some people, when confronted with a problem, think
“I know, I'll use regular expressions.”   Now they have two problems.

Tham khảo

https://docs.python.org/3/library/re.html

https://blog.codinghorror.com/regular-expressions-now-you-have-two-problems/

Hết.

HVN at "học python tại PyMi" https://pymi.vn and https://www.familug.org 

No comments:

Post a Comment