Regex xen kẽ / hoặc toán tử (foo | bar) trong GNU hoặc BSD Sed


28

Tôi dường như không thể làm cho nó hoạt động. Tài liệu về GNU sed nói thoát khỏi đường ống, nhưng điều đó không hiệu quả, cũng không sử dụng đường ống thẳng mà không thoát. Thêm parens làm cho không có sự khác biệt.

$ echo 'cat
dog
pear
banana
cat
dog' | sed 's/cat|dog/Bear/g'
cat
dog
pear
banana
cat
dog

$ echo 'cat
dog
pear
banana
cat
dog' | sed 's/cat\|dog/Bear/g'
cat
dog
pear
banana
cat
dog

Câu trả lời:


33

Theo mặc định,sed sử dụng POSIX Basic Expressions , không bao gồm |toán tử thay thế. Nhiều phiên bản sed, bao gồm GNU và FreeBSD, hỗ trợ chuyển đổi sang Biểu thức chính quy mở rộng , bao gồm |xen kẽ. Cách bạn thực hiện khác nhau: GNU sed sử dụng-r , trong khi FreeBSD , NetBSD , OpenBSDOS X sed sử dụng -E. Các phiên bản khác hầu như không hỗ trợ nó. Bạn có thể dùng:

echo 'cat dog pear banana cat dog' | sed -E -e 's/cat|dog/Bear/g'

và nó sẽ hoạt động trên các hệ thống BSD đó và sed -rvới GNU.


GNU seddường như hoàn toàn không có giấy tờ nhưng hoạt động hỗ trợ -E, vì vậy nếu bạn có một tập lệnh đa nền tảng được giới hạn ở trên thì đó là lựa chọn tốt nhất của bạn. Vì nó không được ghi nhận, nên có lẽ bạn không thể thực sự dựa vào nó.

Một nhận xét lưu ý rằng các phiên bản BSD cũng hỗ trợ -rnhư một bí danh không có giấy tờ. OS X vẫn không có ngày hôm nay và các máy NetBSD và OpenBSD cũ hơn mà tôi có quyền truy cập, nhưng NetBSD 6.1 thì không. Các Unices thương mại tôi có thể tiếp cận phổ biến không. Vì vậy, với tất cả những câu hỏi về tính di động đang trở nên khá phức tạp vào thời điểm này, nhưng câu trả lời đơn giản là chuyển sangawk nếu bạn cần nó, sử dụng ERE ở mọi nơi.


Ba BSD mà bạn đề cập đều hỗ trợ -rtùy chọn này dưới dạng từ đồng nghĩa về -Ekhả năng tương thích với GNU sed. OpenBSD và OS X sed -Esẽ giải thích đường ống thoát là đường ống theo nghĩa đen, không phải là toán tử thay thế. Đây là một liên kết hoạt động đến trang người dùng NetBSD và đây là một liên kết cho OpenBSD chưa được mười năm tuổi.
damien



9

Điều này xảy ra bởi vì (a|b)là một biểu thức chính quy mở rộng, không phải là Biểu thức chính quy cơ bản. Sử dụng -Etùy chọn để đối phó với điều này.

echo 'cat
dog
pear
banana
cat
dog'|sed -E 's/cat|dog/Bear/g'

Từ sedtrang người đàn ông:

 -E      Interpret regular expressions as extended (modern) regular
         expressions rather than basic regular expressions (BRE's).

Lưu ý rằng đó -rlà một cờ khác cho cùng một thứ, nhưng -Edễ mang theo hơn và thậm chí sẽ có trong phiên bản tiếp theo của thông số kỹ thuật POSIX.


6

Cách di động để làm điều này - và cách hiệu quả hơn - là với các địa chỉ. Bạn có thể làm được việc này:

printf %s\\n cat dog pear banana cat dog |
sed -e '/cat/!{/dog/!b' -e '};cBear'

Theo cách này, nếu dòng không chứa chuỗi mèo và không chứa chuỗi chó sed b xuất ra khỏi tập lệnh, hãy tự động in lại dòng hiện tại của nó và kéo vào tiếp theo để bắt đầu chu kỳ tiếp theo. Do đó, nó không thực hiện hướng dẫn tiếp theo - trong ví dụ này ctreo toàn bộ dòng để đọc Bear nhưng nó có thể làm bất cứ điều gì.

Có lẽ đáng chú ý thêm rằng mọi tuyên bố sau khi !bở chỗ sedlệnh có thể chỉ phù hợp trên một dòng có chứa một trong hai chuỗi doghay cat- vì vậy bạn có thể thực hiện xét nghiệm chuyên sâu mà không cần bất kỳ nguy cơ phù hợp với một dòng mà không - có nghĩa là bây giờ bạn có thể áp dụng quy tắc chỉ một hoặc khác là tốt.

Nhưng đó là tiếp theo. Đây là đầu ra từ lệnh trên:

###OUTPUT###
Bear
Bear
pear
banana
Bear
Bear

Bạn cũng có thể triển khai một bảng tra cứu với các phản hồi.

printf %s\\n cat dog pear banana cat dog |
sed '1{x;s/^/ cat dog /;x
};G;s/^\(.*\)\n.* \1 .*/Bear/;P;d'

Việc thiết lập cho trường hợp ví dụ đơn giản này sẽ tốn nhiều công sức hơn, nhưng sedvề lâu dài có thể tạo ra các tập lệnh linh hoạt hơn nhiều .

Trong dòng đầu tiên, tôi xthay đổi không gian giữ và không gian mẫu sau đó chèn chuỗi <space>con <space>chó mèo<space> vào không gian giữ trước khi e xthay đổi chúng trở lại.

Từ đó trở đi, trên mỗi dòng tiếp theo, tôi Gvà không gian được nối vào không gian mẫu, sau đó kiểm tra xem liệu tất cả các ký tự từ đầu dòng cho đến dòng mới tôi vừa thêm vào cuối có khớp với một chuỗi được bao quanh bởi khoảng trắng sau nó không. Nếu vậy tôi thay thế toàn bộ lô bằng Bear và nếu không thì không có tác hại gì vì tôi Pchỉ tiếp tục đến dòng mới xuất hiện đầu tiên trong không gian mẫu sau đó dxóa bỏ tất cả.

###OUTPUT###
Bear
Bear
pear
banana
Bear
Bear

Và khi tôi nói linh hoạt, tôi có nghĩa là nó. Ở đây, nó sẽ thay thế con mèo bằng BrownBearcon chó bằng BlackBear :

printf %s\\n cat dog pear banana cat dog |
sed '1{x;s/^/ 1cat Brown 2dog Black /;x
};G;s/^\(.*\)\n.* [0-9]\1 \([^ ]*\) .*/\2Bear/;P;d'

###OUTPUT###
BrownBear
BlackBear
pear
banana
BrownBear
BlackBear

Tất nhiên bạn có thể mở rộng rất nhiều về nội dung của bảng tra cứu - Tôi đã chọn ý tưởng từ các email sử dụng của Greg Ubben về chủ đề này, vào những năm 90, ông mô tả cách ông xây dựng một máy tính thô sơ trong một sed s///tuyên bố.


1
phew, +1. Tôi có một xu hướng nghĩ ra khỏi hộp tôi phải nói
iruvar

@ 1_CR - Xem bản chỉnh sửa cuối cùng của tôi - không phải ý tưởng của tôi - điều đó không có nghĩa là tôi không đánh giá cao điều đó và coi đó là một lời khen. Nhưng tôi muốn cung cấp tín dụng khi đến hạn.
mikeerv

1

Đây là một câu hỏi khá cũ, nhưng trong trường hợp ai đó muốn thử, có một cách nỗ lực khá thấp để làm điều này trong sed với các tệp sed. Mỗi tùy chọn có thể được liệt kê trên một dòng riêng biệt và sed sẽ đánh giá từng tùy chọn. Đó là một tương đương logic của hoặc. Ví dụ: để xóa các dòng có chứa một mã nhất định:

bạn có thể nói : sed -E '/^\/\*!(40103|40101|40111).*\/;$/d'

hoặc đặt cái này trong tập tin sed của bạn:

/^\/\*!40103.*\/;$/d
/^\/\*!40101.*\/;$/d
/^\/\*!40111.*\/;$/d

0

Dưới đây là một kỹ thuật mà không sử dụng bất kỳ tùy chọn triển khai cụ thể đến sed(ví dụ -E, -r). Thay vì mô tả mẫu như một biểu thức chính cat|dog, chúng ta có thể chạy sedhai lần:

echo 'cat
dog
pear
banana
cat
dog' | sed 's/cat/Bear/g' | sed 's/dog/Bear/g'

Đó là một cách giải quyết rõ ràng thực sự, nhưng đáng để chia sẻ. Nó tự nhiên khái quát thành nhiều hơn hai chuỗi mẫu, mặc dù một chuỗi rất dài sedkhông quá đẹp mắt.

Tôi thường sử dụng sed -i(hoạt động giống nhau trong tất cả các triển khai) để thực hiện các thay đổi trong tệp. Ở đây, một danh sách dài các chuỗi mẫu có thể được kết hợp độc đáo, vì mỗi kết quả tạm thời được lưu vào tệp:

for pattern in cat dog owl; do
    sed -i "s/${pattern}/Bear/g" myfile
done
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.