Số dấu gạch chéo ngược cần thiết để thoát dấu gạch chéo ngược regex trên dòng lệnh


12

Gần đây tôi đã gặp sự cố với một số biểu thức chính trên dòng lệnh và thấy rằng để khớp với dấu gạch chéo ngược, có thể sử dụng số lượng ký tự khác nhau. Con số này phụ thuộc vào trích dẫn được sử dụng cho regex (không có, dấu ngoặc đơn, dấu ngoặc kép). Xem phiên bash sau đây để biết ý tôi là gì:

echo "#ab\\cd" > file
grep -E ab\cd file
grep -E ab\\cd file
grep -E ab\\\cd file
grep -E ab\\\\cd file
#ab\cd
grep -E ab\\\\\cd file
#ab\cd
grep -E ab\\\\\\cd file
#ab\cd
grep -E ab\\\\\\\cd file
#ab\cd
grep -E ab\\\\\\\\cd file
grep -E "ab\cd" file
grep -E "ab\\cd" file
grep -E "ab\\\cd" file
#ab\cd
grep -E "ab\\\\cd" file
#ab\cd
grep -E "ab\\\\\cd" file
#ab\cd
grep -E "ab\\\\\\cd" file
#ab\cd
grep -E "ab\\\\\\\cd" file
grep -E 'ab\cd' file
grep -E 'ab\\cd' file
#ab\cd
grep -E 'ab\\\cd' file
#ab\cd
grep -E 'ab\\\\cd' file

Điều này có nghĩa rằng:

  • không có dấu ngoặc kép, tôi có thể kết hợp dấu gạch chéo ngược với 4-7 dấu gạch chéo ngược thực tế
  • với dấu ngoặc kép, tôi có thể kết hợp dấu gạch chéo ngược với 3-6 dấu gạch chéo ngược thực tế
  • Với dấu ngoặc đơn, tôi có thể kết hợp dấu gạch chéo ngược với 2-3 dấu gạch chéo ngược thực tế

Tôi hiểu rằng một dấu gạch chéo ngược bổ sung bị bỏ qua bởi trình bao (từ trang bash man):

"Dấu gạch chéo ngược không được trích dẫn (\) là ký tự thoát. Nó giữ nguyên giá trị theo nghĩa đen của ký tự tiếp theo sau"

Điều này không áp dụng cho các ví dụ trích dẫn đơn, bởi vì không có lối thoát nào được thực hiện trong dấu ngoặc đơn.

Và một dấu gạch chéo ngược bổ sung bị bỏ qua bởi lệnh grep ("\ c" chỉ là "c" đã thoát, nhưng điều này cũng giống như "c", vì "c" không có ý nghĩa đặc biệt trong regex).

Điều này giải thích hành vi của ví dụ với các trích dẫn đơn, nhưng tôi không thực sự hiểu hai ví dụ khác, đặc biệt là tại sao có sự khác biệt giữa các chuỗi không trích dẫn.

Một lần nữa, một trích dẫn từ trang bash man:

"Việc bao gồm các ký tự trong dấu ngoặc kép sẽ giữ giá trị bằng chữ của tất cả các ký tự trong dấu ngoặc kép, ngoại trừ $,`, \, và, khi mở rộng lịch sử được bật,!. "

Tôi đã thử tương tự với GNU awk (ví dụ awk /ab\cd/{print} file), với kết quả tương tự.

Tuy nhiên, Perl hiển thị các kết quả khác nhau (sử dụng ví dụ perl -ne "/ab\\cd/"\&\&print file):

  • không có dấu ngoặc kép, tôi có thể kết hợp dấu gạch chéo ngược với 4-5 dấu gạch chéo ngược thực tế
  • với dấu ngoặc kép, tôi có thể kết hợp dấu gạch chéo ngược với 3-4 dấu gạch chéo ngược thực tế
  • Với dấu ngoặc đơn, tôi có thể khớp dấu gạch chéo ngược với 2 dấu gạch chéo ngược thực tế

Bất cứ ai cũng có thể giải thích sự khác biệt giữa các chuỗi regex không trích dẫn và hai lần trên dòng lệnh cho grep và awk? Tôi không quan tâm đến lời giải thích về hành vi của Perl, vì tôi thường không sử dụng Perl một lớp.

Câu trả lời:


10

Đối với ví dụ không được trích dẫn, mỗi \\cặp chuyển một dấu gạch chéo ngược sang grep, do đó, 4 dấu gạch chéo ngược chuyển hai đến grep, nghĩa là một dấu gạch chéo ngược. 6 dấu gạch chéo ngược vượt qua ba đến grep, dịch thành một dấu gạch chéo ngược và một dấu gạch chéo \c, tương đương với c. Một dấu gạch chéo ngược bổ sung không thay đổi bất cứ điều gì, bởi vì nó được dịch \c-> cbởi trình bao. Tám dấu gạch chéo ngược trong shell là bốn trong grep, được dịch thành hai, vì vậy điều này không còn phù hợp nữa.

Đối với ví dụ trong dấu ngoặc kép, hãy lưu ý những gì sau trích dẫn thứ hai của bạn từ trang bash:

Dấu gạch chéo ngược chỉ giữ lại ý nghĩa đặc biệt của nó khi được theo sau bởi một trong các ký tự sau: $, `,", \ hoặc dòng mới.

Tức là khi bạn đưa ra một số dấu gạch chéo lẻ, chuỗi kết thúc bằng \c, ctrong trường hợp không được trích dẫn, nhưng khi được trích dẫn, dấu gạch chéo ngược làm mất đi ý nghĩa đặc biệt của nó, do đó \cđược chuyển sang grep. Đó là lý do tại sao phạm vi dấu gạch chéo ngược "có thể" (nghĩa là các dấu gạch chéo tạo thành một mẫu phù hợp với tệp ví dụ của bạn) trượt xuống một.


... và sau đó có một số điều kỳ lạ: ví dụ: printf "\ntest"sẽ chèn một dòng mới trước khi "kiểm tra", mặc dù "\n"đã được dịch sang "n"shell vì đó là dấu ngoặc kép của whithin ... (vì vậy kết quả mong đợi sẽ là, cho "\ ntest", "ntest". Chúng ta nên tập thói quen viết: printf "\\ntest"hoặc printf '\ntest', nhưng bằng cách nào đó tôi thấy rất nhiều kịch bản dựa vào sự kỳ quặc thay vào đó.
Olivier Dulac

6

Liên kết này mô tả bash Báo giá và Thoát

Câu hỏi của bạn đề cập đến ba phần đầu tiên.

  • Mỗi nhân vật trốn thoát
  • Trích dẫn yếu "trích dẫn kép"
  • Trích dẫn mạnh mẽ ' trích dẫn đơn'
  • ANSI C như trích dẫn chuỗi
  • Trích dẫn I18N / L10N (Quốc tế hóa và nội địa hóa) .

Dưới đây là biểu đồ về cách các chuỗi bashchuyển chúng sang grepvà cách greptiếp tục diễn giải chúng trong nội bộ.

Trước tiên hãy nhìn vào echo "#ab\\cd" > file.
Trong phần trích dẫn yếu ("") "#ab\\cd", \\là một lối thoát \được chuyển thành filemột nghĩa đen duy nhất \. Vì vậy, filecó chứa ab\cd

Bây giờ, với các lệnh của bạn: Biểu đồ bên dưới có thể giúp xem thực tế diễn ra với mỗi cuộc gọi. Các *hiển thị những mà phù hợp với nội dung tập tin. Đây thực sự chỉ là vấn đề áp dụng các quy tắc thoát của bash, như trên trang web, đặc biệt lưu ý đến câu trả lời của daniel kullmann , trong đó ông đề cập đến việc thoát khỏi hành vi trong một tình huống trích dẫn yếu .

Dấu gạch chéo ngược chỉ giữ lại ý nghĩa đặc biệt của nó khi được theo sau bởi một trong các ký tự sau: $, `,", \ hoặc dòng mới.


                            bash passes    grep further
                            to grep        resolves to         
grep -E ab\cd file            abcd           abcd   
grep -E ab\\cd file           ab\cd          abcd  
grep -E ab\\\cd file          ab\cd          abcd
grep -E ab\\\\cd file         ab\\cd         ab\cd    * 
grep -E ab\\\\\cd file        ab\\\cd        ab\cd    *
grep -E ab\\\\\\cd file       ab\\\cd        ab\cd    *    
grep -E ab\\\\\\\cd file      ab\\\cd        ab\cd    *
grep -E ab\\\\\\\\cd file     ab\\\\cd       ab\\cd

grep -E "ab\cd" file          ab\cd          abcd
grep -E "ab\\cd" file         ab\cd          abcd
grep -E "ab\\\cd" file        ab\\cd         ab\cd    *
grep -E "ab\\\\cd" file       ab\\cd         ab\cd    *
grep -E "ab\\\\\cd" file      ab\\\cd        ab\cd    *
grep -E "ab\\\\\\cd" file     ab\\\cd        ab\cd    *
grep -E "ab\\\\\\\cd" file    ab\\\\cd       ab\\cd    

grep -E 'ab\cd' file          ab\cd          abcd  
grep -E 'ab\\cd' file         ab\\cd         ab\cd    *
grep -E 'ab\\\cd' file        ab\\\cd        ab\cd    *
grep -E 'ab\\\\cd' file       ab\\\\cd       ab\\cd
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.