Làm thế nào để bạn lưu một regex phức tạp để sử dụng lại nhiều lần trong sed?


12

Khi sử dụng sed, tôi thường tạo các biểu thức khá phức tạp và phức tạp mà tôi cần phải khớp hai lần trong một tệp. Có cách nào để tôi lưu regex này và chỉ cần tham khảo hai lần không?

Có lẽ một cái gì đó trông như thế này?

sed ' complicated_regex=/^(([a-f0-9]{32})+([a-zA-Z0-9=]{{$i}})?)+$/
s/complicated_regex:complicated_regex/simple-output/
' my_file

Cập nhật: Một câu trả lời đã trình bày giải pháp sử dụng biến bash. Điều này không hoạt động. Cho a test.txt.

#test.txt
foo bar
bar foo

Và kịch bản

#!/bin/bash

VALUE='foo \([a-z]\+\)'

sed 's/"${VALUE}"/foo happy \1/' test.txt

Điều này sẽ tạo ra đầu ra

foo happy bar
bar foo

Nhưng thay vào đó tôi nhận được lỗi

sed: -e expression #1, char 24: invalid reference \1 on `s' command's RHS

Nếu bạn sử dụng Perl, bạn có thể tham chiếu các phần trước của biểu thức trong biểu thức:perl -pe 's/^(([a-f0-9]{32})+([a-zA-Z0-9=]{{$i}})?)+:\1$/simple_output/' my_file
glenn jackman

1
Khi bạn tìm thấy một cái gì đó quá phức tạp đối với sed, đừng ngần ngại đi tìm awk hoặc perl.
Gilles 'SO- ngừng trở nên xấu xa'

2
@Cory: Ví dụ của bạn hoạt động tốt, nếu bạn hiểu đúng cú pháp của nó. Bạn có "biến $" vẫn được đặt trong 'dấu ngoặc đơn' ẩn nó khỏi phần mở rộng shell mà bạn mong đợi ... nó sẽ làsed 's/'"${VALUE}"'/foo happy \1/' test.txt
Peter.O

Câu trả lời:


7

Bạn có thể sử dụng các biến shell:

complicated_regex='(([a-f0-9]{32})+([a-zA-Z0-9=]{{$i}})?)+'
sed s/^"$complicated_regex":"$complicated_regex"\$/'simple-output'/ my_file

Tôi không chắc về ý của bạn là gì $i, nhưng bạn có thể cần đặt nó bên ngoài các trích dẫn đơn:

complicated_regex='(([a-f0-9]{32})+([a-zA-Z0-9=]{{'"$i"'}})?)+'

Tất nhiên điều này chỉ hoạt động nếu sedlệnh được gọi từ shell, nhưng có một giải pháp tương tự với hầu hết tất cả các ngôn ngữ lập trình. (Và tôi không nghĩ có thể sử dụng các biến trong sed.)
Stéphane Gimenez

Hrm. Cố gắng này, backreferences dường như bị phá vỡ. s/$complicated_regex/\1/đưa ra một lỗi nói rằng đó là một tài liệu tham khảo không hợp lệ.
Cory Klein

À, có lẽ lỗi của tôi, tôi đã quen với việc thay thế biến zsh. Xem câu trả lời cập nhật.
Stéphane Gimenez

Bạn sẽ phải loại bỏ các neo khỏi biến và đưa chúng vào tập lệnh sed:sed "s/^${complicated_regex}:${complicated_regex}\$/simple-output/" my_file
glenn jackman

Tât nhiên! Có, tôi đã quên kiểm tra rằng tôi đã được cung cấp một kết nối regex hợp lệ :-)
Stéphane Gimenez

0

Cách dễ nhất để giảm giá trị biến shell sedvà không lo lắng về việc thoát dấu gạch chéo ngược của bạn sẽ cần thay đổi như thế nào đối với phần còn lại của sedtập lệnh của bạn , là nhét mọi thứ vào dấu ngoặc đơn trừ biến và đặt dấu ngoặc kép.

Tất cả các ví dụ mã sau đây giả sử: VALUE='foo \([a-z]\+\)'

bị hỏng sau đây không thành công vì biến VALUEkhông được mở rộng:

sed 's/"${VALUE}"/foo happy \1/' test.txt

Đoạn mã bị hỏng sau đây không thành công do dấu gạch chéo ngược \1bị ăn mòn bởi vì nó nằm trong dấu ngoặc kép thay vì dấu ngoặc đơn) trước khi sednhìn thấy nó:

sed "s/${VALUE}/foo happy \1/" test.txt

Các mã sau hoạt động như mong đợi:

sed 's/'"${VALUE}"'/foo happy \1/' test.txt

Mã sau đây cũng hoạt động:

sed "s/${VALUE}/foo happy \\1/" test.txt

Những điều sau đây cũng vậy:

sed s/"${VALUE}"/foo\ happy\ \\1/ test.txt

Nhưng tại sao lại phức tạp? Các trích dẫn đơn xung quanh một sedtập lệnh làm cho mọi thứ rõ ràng hơn nhiều, đặc biệt là đối với các chuyên gia không phải là người viết kịch bản đang đọc mã của bạn. Cách ưa thích của tôi là, một lần nữa, bỏ các dấu ngoặc đơn thành dấu ngoặc kép chỉ để mở rộng biến và chuyển trở lại dấu ngoặc đơn:

sed 's/'"${VALUE}"'/foo happy \1/' test.txt
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.