Làm thế nào tôi có thể khớp một chuỗi với regex trong Bash?


166

Tôi cố gắng để viết một kịch bản bash có chứa một hàm nên khi đưa ra một .tar, .tar.bz2, .tar.gz, vv tập tin nó sử dụng tar với thiết bị chuyển mạch có liên quan để giải nén tập tin.

Tôi đang sử dụng if elif sau đó phát biểu kiểm tra tên tệp để xem nó kết thúc bằng gì và tôi không thể làm cho nó khớp với bằng cách sử dụng siêu ký tự regex.

Để lưu liên tục viết lại tập lệnh mà tôi đang sử dụng 'test' tại dòng lệnh, tôi nghĩ rằng câu lệnh bên dưới sẽ hoạt động, tôi đã thử mọi cách kết hợp dấu ngoặc, dấu ngoặc kép và trình chuyển đổi dữ liệu có thể và vẫn thất bại.

test sed-4.2.2.tar.bz2 = tar\.bz2$; echo $?
(this returns 1, false)

Tôi chắc rằng vấn đề là một vấn đề đơn giản và tôi đã tìm kiếm khắp nơi, nhưng tôi không thể hiểu làm thế nào để làm điều đó. Có ai biết làm thế nào tôi có thể làm điều này?

Câu trả lời:


268

Để phù hợp với biểu thức chính, bạn cần sử dụng =~toán tử.

Thử cái này:

[[ sed-4.2.2.tar.bz2 =~ tar.bz2$ ]] && echo matched

Ngoài ra, bạn có thể sử dụng ký tự đại diện (thay vì biểu thức chính quy) với ==toán tử:

[[ sed-4.2.2.tar.bz2 == *tar.bz2 ]] && echo matched

Nếu tính di động không phải là một mối quan tâm, tôi khuyên bạn nên sử dụng [[thay vì [hoặc testvì nó an toàn và mạnh mẽ hơn. Xem sự khác biệt giữa thử nghiệm, [và [[? để biết chi tiết.


7
Hãy cẩn thận với kết hợp ký tự đại diện toàn cầu trong ví dụ thứ hai. Bên trong [[]], * không được mở rộng như thường lệ, để khớp tên tệp trong thư mục hiện tại khớp với mẫu. Ví dụ của bạn hoạt động, nhưng thực sự quá dễ để khái quát hóa và nhầm lẫn rằng * có nghĩa là khớp với bất cứ thứ gì trong bất kỳ bối cảnh. Nó chỉ hoạt động như thế bên trong [[]]. Mặt khác, nó mở rộng đến tên tệp hiện có.
Alan Porter

7
Tôi đã cố gắng sử dụng dấu ngoặc kép trên regex và thất bại; câu trả lời này đã giúp thực hiện công việc này, check="^a.*c$";if [[ "abc" =~ $check ]];then echo match;fichúng tôi cần lưu trữ regex trên var
Aquarius Power

Ngoài ra, cần lưu ý rằng regrec (như trong perl) KHÔNG phải trong ngoặc đơn: [[ sed-4.2.2.tar.bz2 == "*tar.bz2" ]]sẽ không hoạt động.
pevik 27/2/2015

18
FWIW, cú pháp phủ định (nghĩa là không khớp ) là [[ ! foo =~ bar ]].
Skippy le Grand Gourou

1
dash không hỗ trợ -n 1tham số, nó cũng không tự động đặt nó vào một $REPLYbiến. Coi chừng!

54

Một chức năng để làm điều này

extract () {
  if [ -f $1 ] ; then
      case $1 in
          *.tar.bz2)   tar xvjf $1    ;;
          *.tar.gz)    tar xvzf $1    ;;
          *.bz2)       bunzip2 $1     ;;
          *.rar)       rar x $1       ;;
          *.gz)        gunzip $1      ;;
          *.tar)       tar xvf $1     ;;
          *.tbz2)      tar xvjf $1    ;;
          *.tgz)       tar xvzf $1    ;;
          *.zip)       unzip $1       ;;
          *.Z)         uncompress $1  ;;
          *.7z)        7z x $1        ;;
          *)           echo "don't know '$1'..." ;;
      esac
  else
      echo "'$1' is not a valid file!"
  fi
}

Lưu ý khác

Đáp lại sức mạnh của Bảo Bình trong nhận xét trên, We need to store the regex on a var

Biến BASH_REMATCH được đặt sau khi bạn khớp với biểu thức và $ {BASH_REMATCH [n]} sẽ khớp với nhóm thứ n được gói trong ngoặc đơn, ví dụ như sau ${BASH_REMATCH[1]} = "compressed"${BASH_REMATCH[2]} = ".gz"

if [[ "compressed.gz" =~ ^(.*)(\.[a-z]{1,5})$ ]]; 
then 
  echo ${BASH_REMATCH[2]} ; 
else 
  echo "Not proper format"; 
fi

(Regex ở trên không có nghĩa là hợp lệ để đặt tên và tiện ích mở rộng tệp, nhưng nó hoạt động với ví dụ)


cũng lưu ý rằng với BSD tar, bạn có thể sử dụng "tar xf" cho tất cả các định dạng và không cần các lệnh riêng biệt hoặc chức năng này.
Người tốt

atrên GNU tar hoặc ptrên tar BSD để nói rõ ràng với nó để tự động suy ra kiểu nén từ phần mở rộng. GNU tar sẽ không tự động làm điều đó theo cách khác và tôi đoán từ nhận xét của @oodPerson rằng BSD tar làm điều đó theo mặc định.
Mark K Cowan

7z có thể giải nén .. AR, ARJ, CAB, CHM, CPIO, CramFS, DMG, EXT, FAT, GPT, HFS, IHEX, ISO, LZH, LZMA, MBR, MSI, NSIS, NTFS, QCOW2, RAR, RPM, Squash , UDF, UEFI, VDI, VHD, VMDK, WIM, XAR và Z. xem 7-zip.org
mosh

14

Tôi không có đủ đại diện để bình luận ở đây, vì vậy tôi đang gửi câu trả lời mới để cải thiện câu trả lời của dogbane. Dấu chấm . trong regrec

[[ sed-4.2.2.tar.bz2 =~ tar.bz2$ ]] && echo matched

sẽ thực sự khớp với bất kỳ ký tự nào, không chỉ là dấu chấm theo nghĩa đen giữa 'tar.bz2', chẳng hạn

[[ sed-4.2.2.tar4bz2 =~ tar.bz2$ ]] && echo matched
[[ sed-4.2.2.tar§bz2 =~ tar.bz2$ ]] && echo matched

hoặc bất cứ điều gì không yêu cầu thoát với '\'. Cú pháp chặt chẽ sau đó phải là

[[ sed-4.2.2.tar.bz2 =~ tar\.bz2$ ]] && echo matched

hoặc bạn có thể đi thậm chí chặt chẽ hơn và cũng bao gồm dấu chấm trước trong regex:

[[ sed-4.2.2.tar.bz2 =~ \.tar\.bz2$ ]] && echo matched

9

Vì bạn đang sử dụng bash, bạn không cần tạo quy trình con để thực hiện việc này. Đây là một giải pháp thực hiện nó hoàn toàn trong bash:

[[ $TEST =~ ^(.*):\ +(.*)$ ]] && TEST=${BASH_REMATCH[1]}:${BASH_REMATCH[2]}

Giải thích: Các nhóm trước và sau chuỗi "dấu hai chấm và một hoặc nhiều khoảng trắng" được lưu trữ bởi toán tử khớp mẫu trong mảng BASH_REMATCH.


1
Lưu ý rằng chỉ mục 0 chứa kết quả khớp đầy đủ và chỉ mục 1 và 2 chứa các kết quả khớp nhóm.
Rainer Schwarze

3
if [[ $STR == *pattern* ]]
then
    echo "It is the string!"
else
    echo "It's not him!"
fi

Làm việc cho tôi! GNU bash, version 4.3.11(1)-release (x86_64-pc-linux-gnu)


1
Điều này cực kỳ nguy hiểm; nó chỉ hành xử mà không có hành vi không xác định cho bạn vì bạn không có tệp nào trong thư mục hiện tại có tên là "mẫu" chuỗi con. Hãy tiếp tục, tạo một số tệp có tên như vậy và mở rộng chuỗi con sẽ khớp với các tệp và phá vỡ mọi thứ một cách khủng khiếp với các con bọ cánh cứng nhiều màu.
i336_

Nhưng tôi đã thực hiện một thử nghiệm: với các tệp `1potype, mẫu pattern2 và mẫu trong thư mục hiện tại. Kịch bản này hoạt động như mong đợi. Bạn có thể vui lòng cung cấp cho tôi kết quả kiểm tra của bạn? @ i336_
juan

2
@ i336: Tôi không nghĩ vậy. Trong [[ ... ]], mô hình toàn cầu rhs không mở rộng theo thư mục hiện tại, như thường thấy.
dùng1934428

@ i336_ Không. Trong vòng [[...]], Bash không thực hiện mở rộng tên tệp. Trong hướng dẫn bash,Word splitting and filename expansion are not performed on the words between the [[ and ]];
jinbeom hong

@jinbeomhong: TIL. Đó là điều tốt để biết, cảm ơn!
i336_

2

shopt -s nocasematch

if [[ sed-4.2.2.$LINE =~ (yes|y)$ ]]
 then exit 0 
fi
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.