sử dụng biểu thức chính quy trong if-condition trong bash


88

Tôi tự hỏi quy tắc chung để sử dụng biểu thức chính quy trong mệnh đề if trong bash?

Đây là một ví dụ

$ gg=svm-grid-ch  
$ if [[ $gg == *grid* ]] ; then echo $gg; fi  
svm-grid-ch  
$ if [[ $gg == ^....grid* ]] ; then echo $gg; fi  
$ if [[ $gg == ....grid* ]] ; then echo $gg; fi  
$ if [[ $gg == s...grid* ]] ; then echo $gg; fi  
$   

Tại sao ba cuối cùng không phù hợp?

Hy vọng bạn có thể đưa ra càng nhiều quy tắc chung càng tốt, không chỉ cho ví dụ này.

Câu trả lời:


128

Khi sử dụng mẫu hình cầu, dấu chấm hỏi đại diện cho một ký tự duy nhất và dấu hoa thị biểu thị một chuỗi không hoặc nhiều ký tự:

if [[ $gg == ????grid* ]] ; then echo $gg; fi

Khi sử dụng biểu thức chính quy, dấu chấm biểu thị một ký tự duy nhất và dấu hoa thị đại diện cho không hoặc nhiều ký tự đứng trước. Vì vậy, " .*" đại diện cho không hoặc nhiều ký tự bất kỳ, " a*" đại diện cho không hoặc nhiều hơn "a", " [0-9]*" đại diện cho không hoặc nhiều chữ số. Một hữu ích khác (trong số nhiều) là dấu cộng đại diện cho một hoặc nhiều ký tự đứng trước. Vì vậy, " [a-z]+" đại diện cho một hoặc nhiều ký tự alpha viết thường (trong ngôn ngữ C - và một số ký tự khác).

if [[ $gg =~ ^....grid.*$ ]] ; then echo $gg; fi

Vì vậy, có hai cách so khớp chuỗi: mô hình cầu và biểu thức chính quy? Không phải chỉ dùng cho tên tập tin thôi sao? Trong bash, khi nào sử dụng mẫu hình cầu và khi nào sử dụng biểu thức chính quy? Cảm ơn!
Tim

1
@Tim: Globbing có sẵn trong hầu hết hoặc tất cả các phiên bản của Bash. Đối sánh Regex chỉ có sẵn trong phiên bản 3 trở lên, nhưng tôi khuyên bạn chỉ nên sử dụng nó trong 3.2 trở lên. Regexes là nhiều linh hoạt hơn globbing.
Tạm dừng cho đến khi có thông báo mới.


14
if [[ $gg =~ ^....grid.* ]]

1
Bạn có thể sử dụng ". {4}" thay vì "....", tức là "^. {4} lưới. *". Nó có thể dễ dàng hơn để đọc và hiểu.
user276648

7

Thêm giải pháp này với grepvà các shnội dung cơ bản cho những người quan tâm đến một giải pháp di động hơn (không phụ thuộc vào bashphiên bản; cũng hoạt động với shcác nền tảng cũ , không phải Linux, v.v.)

# GLOB matching
gg=svm-grid-ch    
case "$gg" in
   *grid*) echo $gg ;;
esac

# REGEXP    
if echo "$gg" | grep '^....grid*' >/dev/null ; then echo $gg ; fi    
if echo "$gg" | grep '....grid*' >/dev/null ; then echo $gg ; fi    
if echo "$gg" | grep 's...grid*' >/dev/null ; then echo $gg ; fi    

# Extended REGEXP
if echo "$gg" | egrep '(^....grid*|....grid*|s...grid*)' >/dev/null ; then
  echo $gg
fi    

Một số grephiện thân cũng hỗ trợ -qtùy chọn (yên lặng) như một giải pháp thay thế cho việc chuyển hướng tới /dev/null, nhưng chuyển hướng lại là tùy chọn di động nhất.


quên đóng ")" cho egrep
ghostdog74

5
Sử dụng grep -qthay vì grep >/dev/null.
bfontaine

3

@OP,

Không phải chỉ dùng cho tên tập tin thôi sao?

Không, mẫu "hình cầu" không chỉ được sử dụng cho tên tệp. bạn cũng sử dụng nó để so sánh các chuỗi. Trong các ví dụ của mình, bạn có thể sử dụng chữ hoa / chữ thường để tìm các mẫu chuỗi.

 gg=svm-grid-ch 
 # looking for the word "grid" in the string $gg
 case "$gg" in
    *grid* ) echo "found";;
 esac

 # [[ $gg =~ ^....grid* ]]
 case "$gg" in ????grid*) echo "found";; esac 

 # [[ $gg =~ s...grid* ]]
 case "$gg" in s???grid*) echo "found";; esac

Trong bash, khi nào sử dụng mẫu hình cầu và khi nào sử dụng biểu thức chính quy? Cảm ơn!

Regex linh hoạt và "tiện lợi" hơn "hình cầu", tuy nhiên, trừ khi bạn đang thực hiện các tác vụ phức tạp mà "hình cầu / hình cầu mở rộng" không thể cung cấp dễ dàng, thì không cần sử dụng regex. Regex không được hỗ trợ đối với phiên bản bash <3.2 (như dennis đã đề cập), nhưng bạn vẫn có thể sử dụng chế độ cầu vồng mở rộng (bằng cách cài đặt extglob). để mở rộng cầu vồng, hãy xem tại đây và một số ví dụ đơn giản tại đây .

Cập nhật cho OP: Ví dụ để tìm tệp bắt đầu bằng 2 ký tự (dấu chấm "." Có nghĩa là 1 ký tự) theo sau là "g" bằng cách sử dụng regex

ví dụ đầu ra

$ shopt -s dotglob
$ ls -1 *
abg
degree
..g

$ for file in *; do [[ $file =~ "..g" ]] && echo $file ; done
abg
degree
..g

Ở trên, các tệp được khớp vì tên của chúng chứa 2 ký tự theo sau là "g". (tức là ..g).

Tương đương với globbing sẽ là một cái gì đó như thế này: (xem tham chiếu để biết nghĩa của ?*)

$ for file in ??g*; do echo $file; done
abg
degree
..g

Cảm ơn ghostdog74. Trong Bash với phiên bản cao hơn 3.2, biểu thức chính quy có thể được sử dụng để thay thế mẫu hình cầu ở bất cứ nơi nào xuất hiện sau không? Hay biểu thức chính quy chỉ có thể được sử dụng trong một số trường hợp đặc biệt? Ví dụ: tôi thấy rằng "ls ?? g" đang hoạt động trong khi "ls ..g" thì không.
Tim

Không có gì ngăn cản bạn sử dụng regex nếu có nhu cầu. Tuỳ bạn. Lưu ý, cú pháp regex khác với cú pháp shell globbing. vì vậy ls ..gkhông hoạt động. Bạn đang yêu cầu trình bao tìm kiếm một tệp được đặt tên ..g. Đối với tìm hiểu về cú pháp regex, bạn có thể thử perldoc perlretut, perldoc perlrequickhoặc làm một info sedtrên dòng lệnh.
ghostdog 74
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.