Tại sao không thể tìm thấy -regex khớp với một dòng mới?


7

Tại sao điều này thất bại?

touch "$(printf "a\nb")"; find . -regex './.\n.'

Tôi cũng đã thử những thứ này, không cái nào trong số đó hoạt động:

find . -regextype posix-extended -regex '.\n.'
find . -regextype posix-awk -regex '.\n.'
find . -regextype posix-basic -regex '.\n.'
find . -regextype posix-egrep -regex '.\n.'

Cách duy nhất có vẻ hoạt động là (cảm ơn @MichaelMrozek)

find . -regex './.'$'\n''.'

Đó là rườm rà để nói rằng ít nhất. Vì vậy, tại sao các biểu thức thông thường của tìm thấy dường như không thể đối phó với \n?


Cập nhật để trả lời cho câu trả lời cho đến nay:

OK, tôi hiểu rằng đó \nkhông phải là một phần của ERE và đó là một trong những hiểu lầm của tôi nhưng findyêu cầu hỗ trợ posix-awkvà cả hai gawkmawkphù hợp \nnhư mong đợi:

$ printf "f1l1\nhas newline:f2l1#f1l2 does not:f2l2#" | 
    mawk -F: 'BEGIN{RS="#"}; ($1~/\n/){print $1}' 
f1l1
has newline

Tôi không có awkthử nghiệm thuần túy để có lẽ POSIX awkkhông khớp? Nếu không thì findkhông thực sự thực hiện posix-awkcác biểu thức chính quy?


find . -name $'*\n*'cồng kềnh quá không?
devnull

Không hề, chỉ là không liên quan. Tôi tự hỏi tại sao -regexkhông thành công, không làm thế nào để tìm tập tin có chứa dòng mới trong tên tệp? mà bạn đã trả lời hoàn hảo :).
terdon

Sau khi thảo luận điều này với một số người dùng khác trong trò chuyện, có vẻ như awkngôn ngữ regex không biết \nnhưng trình awkthông dịch thực hiện và đó là lý do tại sao nó phù hợp. Vì vậy, thực hiện awkregexes như findkhông, sẽ không có nghĩa là \nphải phù hợp. Cảm ơn tất cả!
terdon

Câu trả lời:


14

Bởi vì GNU find không hỗ trợ \nnhư một chuỗi thoát. Các regrec \nphù hợp với nhân vật n. GNU tìm bản sao cú pháp Emacs truyền thống, không có tính năng này.

Trong khi GNU find hỗ trợ cú pháp regex khác, không hỗ trợ backslash-letter hoặc backslash-octal để biểu thị các ký tự điều khiển. Bạn cần bao gồm ký tự điều khiển theo nghĩa đen trong đối số.

Có nhiều cú pháp regex khác nhau xung quanh. Cả biểu thức chính quy cơ bản POSIX (BRE) cũng không phải biểu thức chính quy mở rộng (ERE) bao gồm \nhoặc thoát dấu gạch chéo-bát phân. Cả hai định nghĩa đều để lại ý nghĩa của dấu gạch chéo ngược khi không được theo sau bởi một ký tự đặc biệt không xác định. Các tiện ích awksed đều hỗ trợ \ncho một dòng mới; điều này là cụ thể cho các tiện ích này (và phổ biến, nhưng như bạn thấy không phổ biến).

Từ một kịch bản shell, bạn có thể viết

find . -regex $'./.\n.'     # ksh/bash/zsh only
find . -regex './.
.'
find . -name '*
*'

¹ Khá hợp lý: để sử dụng tương tác, bạn có thể gõ bất kỳ ký tự với C-q; để sử dụng lập trình, \ntồn tại như một phần của cú pháp chuỗi ký tự.


Là các biểu thức chính quy cơ bản và các biểu thức chính quy mở rộng không phải là loại được sử dụng trong grep?
Melab

@Melab grepsử dụng BRE theo mặc định hoặc ERE với tùy chọn -E.
Gilles 'SO- ngừng trở nên xấu xa'

6

Bạn không thể khớp dòng mới với '\ n' vì nó không có ý nghĩa đặc biệt trong biểu thức chính quy (ví dụ: ngắt dòng), nhưng bạn có thể khớp phần cuối của dòng với biểu thức $ thông thường.


\nHầu hết chắc chắn có một ý nghĩa trong một regex, hãy thử printf "aa\nbb" | perl -ne 'print if /\n/', điều đó sẽ chỉ phù hợp aa\nvà bỏ qua bbví dụ. Dường như có sự khác biệt trong việc thực hiện mặc dù nguyên nhân grep -Psẽ không phù hợp với điều đó. Nhưng làm thế nào $có liên quan ở đây? Tôi muốn khớp với một dòng mới, $phù hợp ngay cả khi không có:printf "aa" | grep 'a$'
terdon

1
@terdon \nkhông có ý nghĩa đặc biệt, ngay cả trong các biểu thức chính quy Perl. Tuy nhiên, nó có ý nghĩa đặc biệt trong các chuỗi perl nội suy, trong đó qr//là một loại. Tìm kiếm \ntrong man perlre...
derobert

@derobert điểm công bằng, tôi thể hiện bản thân rất tệ. Tôi có nghĩa là \nphù hợp với dòng mới trong biểu thức thông thường. Bạn và babasloves bạn hoàn toàn đúng rằng nó không có ý nghĩa đặc biệt như vậy, tôi chỉ có nghĩa là "phù hợp".
terdon

2
@terdon Chà, ngoại trừ bạn đang cố gắng khớp với ký tự 0x0A (dòng mới) và bạn đang cố gắng thực hiện với chuỗi ký tự 0x5C (dấu gạch chéo ngược) 0x6E (n). Vì \ n không có ý nghĩa đặc biệt, nó cố gắng khớp chính nó. \ Có thể hoặc không thể bị loại bỏ (thoát không hợp lệ) tùy thuộc vào công cụ RE, nhưng bạn đang cố khớp <NL> vs \ n hoặc n, không khớp.
derobert

2
@terdon trong ví dụ Perl của bạn, điều thực sự xảy ra là phân tích chuỗi đang chuyển \nthành <NL>, trước khi chuyển nó sang công cụ regrec. Đó là một tính năng của phân tích chuỗi Perl.
derobert

1

Tôi nghĩ bởi vì findsử dụng fnmatchhàm trong thư viện C tiêu chuẩn, vì vậy nếu FNM_NOESCAPEkhông được đặt, một ký tự dấu gạch chéo ngược theo mẫu có bất kỳ ký tự nào khác sẽ khớp với ký tự thứ hai trong chuỗi.

FNM_NOESCAPE

Don't treat the `\' character specially in patterns. Normally, `\' quotes
the following character, turning off its special meaning (if any) so that it 
matches only itself. When quoting is enabled, the pattern `\?' matches only 
the string `?', because the question mark in the pattern acts like an 
ordinary character. If you use FNM_NOESCAPE, then `\' is an ordinary character.

Tôi kiểm tra với find (GNU findutils) 4.4.2glibc 2.15, tùy chọn này là tắt. kiểm tra line 42tại fnmatch.h:

#define FNM_NOESCAPE    (1 << 1) /* Backslashes don't quote special chars.  */

fnmatchdành cho *.txtkiểu mẫu, không phải .*\.txt$kiểu regexps.
Stéphane Chazelas
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.