Nhầm lẫn bởi đầu ra sed khi sử dụng N. Ai đó có thể giải thích những kết quả này?


8

Tôi đang học sed. Mọi thứ dường như sẽ ổn cho đến khi tôi bắt gặp N (đa dòng tiếp theo). Tôi đã tạo tệp này (guide.txt) cho mục đích thực hành / hiểu / bối cảnh. Đây là nội dung của tập tin nói ...

This guide is meant to walk you through a day as a Network
Administrator. By the end, hopefully you will be better
equipped to perform your duties as a Network Administrator
and maybe even enjoy being a Network Administrator that much more.
Network Administrator
Network Administrator
I'm a Network Administrator

Vì vậy, mục tiêu của tôi là thay thế TẤT CẢ các phiên bản của "Quản trị viên mạng" bằng "Người dùng hệ thống". Vì phiên bản đầu tiên của "Quản trị viên mạng" được phân tách bằng một dòng mới (\ n), tôi cần toán tử tiếp theo nhiều dòng (N) để nối thêm dòng bắt đầu bằng "Quản trị viên" với dòng trước kết thúc bằng "Mạng \ n" . Không vấn đề gì. Nhưng tôi cũng muốn bắt tất cả các trường hợp một dòng "Quản trị viên mạng" khác.

Từ nghiên cứu của tôi, tôi đã học được rằng tôi sẽ cần hai lệnh thay thế; một cho chuỗi phân tách dòng mới và một cho các chuỗi khác. Ngoài ra, có một số jive xảy ra vì dòng cuối cùng chứa kết quả thay thế và đa dòng tiếp theo. Vì vậy, tôi tạo ra ...

$ sed '
> s/Network Administrator/System User/
> N
> s/Network\nAdministrator/System\nUser/
> ' guide.txt

Điều này trả về những kết quả này ...

This guide is meant to walk you through a day as a System
User. By the end, hopefully you will be better
equipped to perform your duties as a System User
and maybe even enjoy being a Network Administrator that much more.
System User
Network Administrator
I'm a System User

Tôi nghĩ rằng sự thay thế một dòng sẽ bắt được tất cả các trường hợp "bình thường" của "Quản trị viên mạng" và trao đổi nó thành "Người dùng hệ thống", trong khi câu lệnh nhiều dòng sẽ hoạt động kỳ diệu trên trường hợp được phân tách dòng mới, nhưng như bạn Có thể thấy nó trở lại, những gì tôi xem xét, kết quả bất ngờ.

Sau một vài lần nghịch ngợm, tôi đáp xuống ...

$ sed '
> s/Network Administrator/System User/
> N
> s/Network\nAdministrator/System\nUser/
> s/Network Administrator/System User/
> ' guide.txt

Và voilà, tôi nhận được đầu ra mong muốn của ...

This guide is meant to walk you through a day as a System
User. By the end, hopefully you will be better
equipped to perform your duties as a System User
and maybe even enjoy being a System User that much more.
System User
System User
I'm a System User

Tại sao điều này hoạt động và kịch bản sed ban đầu không? Tôi thực sự muốn hiểu điều này.

Cảm ơn trước sự giúp đỡ nào.


Tốt cho bạn để học Sed! Tôi đã sử dụng Sed để giải quyết một câu hỏi tương tự trước đó trên trang web này; có thể được quan tâm.
tự đại diện

Câu trả lời:


6

Khi bạn đang học sed, tôi sẽ dành thời gian để thêm vào câu trả lời của @ John1024:

1) Xin lưu ý rằng bạn đang sử dụng \ntrong chuỗi thay thế. Điều này hoạt động trong GNU sed, nhưng không phải là một phần của POSIX, vì vậy nó sẽ chèn một dấu gạch chéo ngược và ntrong nhiều seds khác (sử dụng \ntrong mẫu là xách tay, btw).

Thay vì điều này tôi khuyên bạn nên làm s/Network\([[:space:]]\)Administrator/System\1Us‌​er/g: [[:space:]]Sẽ phù hợp với dòng mới hoặc khoảng trắng, vì vậy bạn không cần hai slệnh, nhưng kết hợp chúng thành một. Bằng cách bao quanh nó, \(...\)bạn có thể tham khảo nó trong phần thay thế: Ý \1chí sẽ được thay thế bằng bất cứ thứ gì được khớp trong cặp đầu tiên \(\).

2) Để khớp đúng các mẫu trên hai dòng, bạn nên biết N;P;Dmẫu:

 sed '$!N;s/Network\([[:space:]]\)Administrator/System\1User/g;P;D'

Các Nluôn là append dòng tiếp theo (trừ dòng cuối cùng, đó là lý do tại sao nó "giải quyết" với $!(= nếu không dòng cuối cùng, bạn nên luôn luôn xem xét để preceed Nvới $!để tránh vô tình kết thúc kịch bản) Sau đó, sau khi thay thế các. PIn chỉ dòng đầu tiên trong không gian mẫu và Dxóa dòng này và bắt đầu chu trình tiếp theo với phần còn lại của không gian mẫu (không đọc dòng tiếp theo). Đây có lẽ là những gì bạn dự định ban đầu.

Hãy nhớ mẫu này, bạn sẽ thường cần nó.

3) Một mẫu hữu ích khác để chỉnh sửa nhiều dòng, đặc biệt là khi có nhiều hơn hai dòng: Giữ thu thập không gian, như tôi đã đề xuất với John:

sed 'H;1h;$!d;g;s/Network\([[:space:]]\)Administrator/System\1Us‌​er/g'

Tôi nhắc lại để giải thích nó: Hnối từng dòng vào không gian giữ. Vì điều này sẽ dẫn đến một dòng mới bổ sung trước dòng đầu tiên, dòng đầu tiên cần phải được di chuyển thay vì được nối thêm 1h. Điều sau đây $!dcó nghĩa là "cho tất cả các dòng trừ dòng cuối cùng, xóa không gian mẫu và bắt đầu lại". Do đó, phần còn lại của tập lệnh chỉ được thực thi cho dòng cuối cùng. Tại thời điểm này, toàn bộ tệp được thu thập trong không gian giữ (vì vậy đừng sử dụng tệp này cho các tệp rất lớn!) Và gdi chuyển tệp sang không gian mẫu, do đó bạn có thể thực hiện tất cả các thay thế cùng một lúc với -ztùy chọn GNU sed.

Đây là một mô hình hữu ích khác tôi đề nghị ghi nhớ.


Ồ Giải thích tuyệt vời! Điều này cùng với câu trả lời của John thực sự đã cho tôi cái nhìn sâu sắc hơn về vấn đề này và sed nói chung. Có vẻ như tôi đã học được nhiều hơn. Tôi ước tôi có thể kiểm tra cả hai giải pháp của bạn như là câu trả lời. Cảm ơn rất nhiều cho cả những nỗ lực của bạn. Họ được nhiều đánh giá cao.
dlowrie290

7

Đầu tiên, lưu ý rằng giải pháp của bạn không thực sự hiệu quả. Xem xét tệp thử nghiệm này:

$ cat test1
Network
Administrator Network
Administrator

Và sau đó chạy lệnh:

$ sed '
 s/Network Administrator/System User/
 N
 s/Network\nAdministrator/System\nUser/
 s/Network Administrator/System User/
 ' test1
System
User Network
Administrator

Vấn đề là mã không thay thế cuối cùng Network\nAdministrator.

Giải pháp này không hoạt động:

$ sed ':a; /Network$/{$!{N;ba}}; s/Network\nAdministrator/System\nUser/g; s/Network Administrator/System User/g' test1
System
User System
User

Chúng tôi cũng có thể áp dụng điều này cho bạn guide.txt:

$ sed ':a; /Network$/{$!{N;ba}}; s/Network\nAdministrator/System\nUser/g; s/Network Administrator/System User/g' guide.txt 
This guide is meant to walk you through a day as a System
User. By the end, hopefully you will be better
equipped to perform your duties as a System User
and maybe even enjoy being a System User that much more.
System User
System User
I'm a System User

Điều quan trọng là tiếp tục đọc theo dòng cho đến khi bạn tìm thấy một dòng không kết thúc Network. Khi điều đó được thực hiện, sự thay thế có thể được thực hiện.

Tương thích Lưu ý: Tất cả các sử dụng ở trên \ntrong văn bản thay thế. Điều này đòi hỏi GNU sed. Nó sẽ không hoạt động trên sed BSD / OSX.

[Mũ cho Philippos .]

Phiên bản đa dòng

Nếu nó giúp làm rõ, đây là cùng một lệnh được chia thành nhiều dòng:

$ sed ':a
    /Network$/{
       $!{
           N
           ba
       }
    }
    s/Network\nAdministrator/System\nUser/g
    s/Network Administrator/System User/g
    ' filename

Làm thế nào nó hoạt động

  1. :a

    Điều này tạo ra một nhãn hiệu a.

  2. /Network$/{ $!{N;ba} }

    Nếu dòng này kết thúc bằng Network, thì, nếu đây không phải là dòng cuối cùng ( $!) đọc và nối dòng tiếp theo ( N) và phân nhánh trở lại nhãn a( ba).

  3. s/Network\nAdministrator/System\nUser/g

    Thực hiện thay thế với dòng mới trung gian.

  4. s/Network Administrator/System User/g

    Thực hiện thay thế với trống trung gian.

Giải pháp đơn giản hơn (chỉ GNU)

Với GNU sed ( không phải BSD / OSX), chúng ta chỉ cần một lệnh thay thế:

$ sed -zE 's/Network([[:space:]]+)Administrator/System\1User/g' test1
System
User System
User

Và trên guide.txthồ sơ:

$ sed -zE 's/Network([[:space:]]+)Administrator/System\1User/g' guide.txt 
This guide is meant to walk you through a day as a System
User. By the end, hopefully you will be better
equipped to perform your duties as a System User
and maybe even enjoy being a System User that much more.
System User
System User
I'm a System User

Trong trường hợp này, hãy -zbảo sed đọc tối đa ký tự NUL đầu tiên. Vì các tệp văn bản không bao giờ có ký tự null, nên điều này có tác dụng đọc toàn bộ tệp cùng một lúc. Sau đó chúng ta có thể thay thế mà không lo bỏ lỡ một dòng.

Phương pháp này không tốt nếu tệp rất lớn (thường có nghĩa là gigabyte). Nếu nó lớn như vậy, thì việc đọc tất cả cùng một lúc có thể làm căng RAM hệ thống.

Giải pháp hoạt động trên cả GNU và BSD sed

Theo đề xuất của Phillipos , sau đây là một giải pháp di động:

sed 'H;1h;$!d;x;s/Network\([[:space:]]\)Administrator/System\1Us‌​er/g'

1
Thông tin tuyệt vời, John! Cảm ơn đã làm sáng tỏ điều này và giải pháp thay thế của bạn là rất tốt đẹp. Điều đó đang được nói, tôi vẫn không hiểu tại sao giải pháp của tôi không phải là một giải pháp. Nó dường như hoạt động, nhưng với tệp test.txt của bạn thì không. Tại sao giải pháp của tôi có vẻ hiệu quả, nhưng thực sự không? Cảm ơn rất nhiều vì sự giúp đỡ.
dlowrie290

1
@ dlowrie290 Giải pháp của bạn đọc theo dòng theo cặp. Nếu Network Administratorđược phân chia giữa dòng thứ nhất và thứ hai của cặp đó, giải pháp của bạn thực hiện thay thế thành công. Sau đó, nó in hai dòng đó và đọc trong cặp tiếp theo. Tuy nhiên, nếu dòng thứ hai của cặp thứ nhất kết thúc bằng Networkvà dòng đầu tiên của cặp thứ hai bắt đầu bằng Administrator, mã sẽ bỏ lỡ nó. Mã của tôi tránh điều này bằng cách đọc các dòng cho đến khi nó tìm thấy một dòng không kết thúc Network.
John1024

2
Xin lưu ý rằng giải pháp đa dòng đầu tiên của bạn cũng phụ thuộc vào các phần mở rộng GNU để sed: Việc \nthay thế không được xác định trong tiêu chuẩn. sed 'H;1h;$!d;x;s/Network\([[:space:]]\)Administrator/System\1User/g'là một cách di động để làm điều đó.
Phi

@Philippos Điểm tuyệt vời. Trả lời cập nhật để bao gồm các giải pháp di động.
John1024

1
Cảm ơn đã làm rõ, John! Một lần nữa, công cụ tuyệt vời và thời gian / nỗ lực của bạn được đánh giá cao nhiều!
dlowrie290
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.