SED thay thế trên nhiều dòng


10

Tôi đang cố gắng tìm ba dòng này:

<!--
<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
-->

và thay thế chúng bằng:

<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />

Khi tôi cố gắng

sudo sed -i 's:<!-- <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" /> -->:<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />:' /myfile.xml

Nó không tìm thấy nó, tôi cũng đã cố gắng đặt \ n vào nó, nhưng nó vẫn không hoạt động:

sudo sed -i 's:<!--\n <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />\n -->:<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />:' /myfile.xml

Chúng không ném bất kỳ lỗi nào, nó chỉ không bỏ sót dòng. Mọi lời khuyên sẽ giúp cảm ơn!

Câu trả lời:


6

sed lần lượt đọc từng dòng, vì vậy nó sẽ không bao giờ khớp với mẫu đa dòng trừ khi bạn đẩy nó theo đúng hướng. Các Nlệnh đọc một dòng từ đầu vào và gắn nó vào không gian mẫu.

sed -i -e '/^<!--$/ {
    N; /\n<Connector port="8009" protocol="AJP\/1\.3" redirectPort="8443" \/>$/ {
        N; /\n-->$/ {
            s/^<!--\n//; s/\n-->$//
        }
    }
}' /myfile.xml

Có thể cho rằng, nếu bạn cần một lệnh khác s, thì bạn nên chuyển từ sed và sang awk hoặc perl. Đây là một đoạn Perl linh hoạt hơn một chút để đối phó với các nhận xét đa dòng theo cách tổng quát hơn.

perl -i -pe '
    if (/<!--/) { $_ .= <> while !/-->/;
        s[<!--\n(<Connector port="8009" protocol="AJP/1\.3" redirectPort="8443" />)\n-->][$1];
    }' /myfile.xml

6

Sed hoạt động trên cơ sở từng dòng. Nó có thể được tạo ra để hoạt động trên nhiều dòng, nhưng nó không được thiết kế theo cách đó - và theo tôi, nó chắc chắn hiển thị khi bạn cố gắng sử dụng như vậy. Nhưng nếu bạn quyết định đi theo cách đó, bạn có thể sẽ phải sử dụng các thanh ghi. Kiểm tra một số giải pháp cho /programming/1251999/sed-how-can-i-replace-a-newline-n để xem cách thực hiện.

Tôi thích sử dụng perl thay vì sed cho loại nhiệm vụ này (ý tôi là đa dòng theo định hướng). Bản tóm tắt bạn phải thêm trước khi tìm kiếm và thay thế ( BEGIN...) không rõ ràng, nhưng regex có vẻ sạch hơn đối với tôi:

perl -i.bak -pe 'BEGIN{undef $/;} s/<!--string-->/string/smg' file.xml

Hoặc, sử dụng nhóm để rút ngắn biểu thức và cho phép bạn sử dụng biểu thức chính ở đó:

perl -i.bak -pe 'BEGIN{undef $/;} s/<!--(string_or_regex)-->/\1/smg' file.xml

Nó nên hoạt động cả trên các lần xuất hiện có và không có dòng mới giữa các đánh dấu nhận xét và mã không bị thiếu.

Chuyển thể từ:

/programming/1030787/multiline-search-replace-with-perl


Cảm ơn sự giúp đỡ của bạn, có vẻ như đó là một nỗi đau, tôi quyết định sử dụng một cách giải quyết
Doug Molineux


1
  • /<\!--/ : chuỗi phù hợp
  • :X : đây là nhãn cho lệnh nhánh "b"
  • /-->/ : chuỗi phù hợp
  • s@...@...@p : dải "<! -", "->" và in kết quả
  • d : xóa không gian mẫu và bắt đầu chu kỳ mới
  • N : nếu không khớp với / -> / sau đó nối một dòng
  • bX : chi nhánh đến: nhãn X
  • p : chỉ in một chuỗi không khớp với / <! - /

sed -rn '
/<!--/ {
    :X
    /-->/ {
        s@<!--\s*(<.+/>)\s*-->@\1@p
        d
    }
    N
    bX
};p'

và phương pháp thứ hai này là một bản sao đơn giản và dán thay thế nguyên văn cho các tệp văn bản có kích thước nhỏ thông thường (cần một tệp script shell)

#!/bin/bash

# copy & paste content that you want to substitute

AA=$( cat <<\EOF | sed -z -e 's#\([][^$*\.#]\)#\\\1#g' -e 's#\n#\\n#g'
<!--
<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
-->
EOF
)

BB=$( cat <<\EOF | sed -z -e 's#\([&\#]\)#\\\1#g' -e 's#\n#\\n#g'
<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
EOF
)

sed -z -i 's#'"${AA}"'#'"${BB}"'#g' *.xml   # apply to all *.xml files

1) Cảm ơn câu trả lời của bạn. 2) Chỉ chèn các đoạn mã không thực sự ổn, sẽ tốt hơn nhiều nếu bạn giải thích, nó làm gì và làm như thế nào.
peterh - Phục hồi Monica
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.