Làm thế nào để phát hiện cuối dòng với sed


14

Tôi đang tìm cách chỉ thực hiện thay thế khi nhân vật cuối cùng là một dòng mới, sử dụng sed.

Ví dụ:

lettersAtEndOfLine

được thay thế, nhưng đây không phải là:

lettersWithCharacterAfter&

sedkhông hoạt động tốt với các dòng mới, nó không đơn giản như

$ sed -E "s/[a-zA-Z]*\n/replace/" file.txt

Làm thế nào điều này có thể được thực hiện?

Câu trả lời:


20

Với tiêu chuẩn sed, bạn sẽ không bao giờ thấy một dòng mới trong văn bản được đọc từ một tập tin. Điều này là do sedđọc từng dòng và do đó không có dòng mới ở cuối văn bản của dòng hiện tại trong sedkhông gian mẫu của. Nói cách khác, sedđọc dữ liệu được phân định bằng dòng mới và các dấu phân cách không phải là một phần của những gì sedtập lệnh nhìn thấy.

Các biểu thức thông thường có thể được neo ở cuối dòng bằng cách sử dụng $(hoặc ở đầu, sử dụng ^). Việc neo một biểu thức ở đầu / cuối của một dòng buộc nó phải khớp chính xác ở đó, và không chỉ bất kỳ nơi nào trên dòng.

Nếu bạn muốn thay thế bất cứ thứ gì khớp với mẫu [A-Za-z]*ở cuối dòng bằng thứ gì đó, thì hãy neo mẫu đó như sau:

[A-Za-z]*$

... sẽ buộc nó khớp ở cuối dòng và không ở đâu khác.

Tuy nhiên, vì [A-Za-z]*$cũng không khớp với (ví dụ: chuỗi trống có ở cuối mỗi dòng), bạn cần buộc khớp một thứ gì đó , ví dụ bằng cách chỉ định

[A-Za-z][A-Za-z]*$

Vì vậy, dòng lệnh sed của bạn sẽ như vậy

$ sed 's/[A-Za-z][A-Za-z]*$/replace/' file.txt

Tôi không sử dụng công -Etắc ở đây vì không cần thiết. Với nó, bạn có thể đã viết

$ sed -E 's/[A-Za-z]+$/replace/' file.txt

Đó là một vấn đề của hương vị.


Mặc dù tôi biết cách thực hiện điều đó, bạn sẽ nhận được +1 chỉ bằng cách sử dụng thuật ngữ kỹ thuật cho nó. :) Vì vậy, điều này được gọi là neo - tốt đẹp để biết. Cho đến bây giờ, tôi luôn phải diễn giải nó ... Một lưu ý khác về+ : bạn CÓ THỂ sử dụng nó ngay cả khi không sử dụng regex mở rộng, chỉ cần nhớ viết nó như thế nào \+. Vì vậy, sed -e 's/[A-Za-z]\+$/replace/' file.txtsẽ hoạt động hoàn hảo ngay cả khi không sedcài đặt GNU . Và không được quên: Không sử dụng -E, vì GNU sedkhông hỗ trợ nó .
cú pháp

1
@syntaxerror - Tôi nghĩ bạn có thể xóa câu cuối cùng hoặc ít nhất là không được trả lời vì nó gnu sedchắc chắn hỗ trợ-E .
don_crissti

@don_crissti Chà, tôi nghĩ bạn đã ở trên mạng này đủ lâu để biết rằng không có cách nào để bỏ các phần của một nhận xét (trừ khi bạn viết lại hoàn toàn). Vì vậy, hãy để tôi sửa thành: GNU sedcó thể "âm thầm" hỗ trợ -E, nhưng nó không được ghi lại trong trang hướng dẫn (cũng như trong hướng dẫn Texinfo (đã kiểm tra cả hai)). Do đó tôi cho rằng nó không được hỗ trợ (rốt cuộc đó là một giả định sai). Dù sao, bạn đúng, vì ít nhất GNU sedsẽ không phàn nàn nếu bạn sử dụng tùy chọn này.
cú pháp

@don_crissti Rất vui vì bạn đã làm! Vì vậy, ít nhất nó đã được xác nhận rằng sed sẽ có một tùy chọn cụ thể chưa được ghi lại đúng cách. Điều này luôn có ích; nếu không ai biết về việc thiếu tài liệu, sẽ không có ai sửa nó.
cú pháp

@syntaxerror, xem unix.stackexchange.com/a/310454/135943 . Tất nhiên, nếu bạn phải làm việc với các hệ thống cũ như RHEL 5, thì bạn sẽ sử dụng phiên bản GNU sed không hỗ trợ -E.
tự đại diện

3
sed "s/[a-zA-Z]*$/replace/" input.txt > result.txt

Hoặc, cách phức tạp dài không cần thiết:

Tôi đã phát hiện ra, điều này có thể được thực hiện, vẫn sử dụng sed, với sự giúp đỡ của tr. Bạn có thể chỉ định một ký tự khác để thể hiện kết thúc của dòng. Một ký tự tạm thời khác phải được sử dụng, trong trường hợp này là "` ". Hãy sử dụng "~" để thể hiện phần cuối của dòng:

tr '\n' '`' <input.txt >output.txt
sed -i "s/`/~`/" output.txt
tr '`' '\n' <output.txt >result.txt

Và sau đó để thực hiện tìm kiếm thực tế và thay thế, hãy sử dụng "~" thay vì "\ n":

sed -i -E "s/[a-zA-Z]*~/replace/" result.txt

Và sau đó dọn sạch ký tự phụ trên các dòng khác:

sed -i "s/~//" result.txt

Rõ ràng, tất cả điều này có thể được kết hợp với nhau dẫn đến một cái gì đó như:

tr '\n' '`' <input.txt | sed -e "s/`/~`/" | tr '`' '\n' | sed -E -e "s/[a-zA-Z]*~/replace/" | sed "s/~//" > result.txt

3
Không chắc chắn tôi hiểu ... Tại sao bạn không neo đến cuối dòng $? ví dụs/[a-zA-Z]*$/replace/
don_crissti

1
2 điểm: 1) Bạn nên sử dụng tốt hơn \+thay vì *từ sau cho phép các chữ cái 0 ở cuối chuỗi; 2) Bạn có thể sử dụng một lớp nhân vật [[:alpha:]]. Vì vậy:sed 's/[[:alpha:]]\+$/replace/' file
glenn jackman

@glennjackman Dấu gạch chéo ngược trước dấu cộng là gì? Điều đó sẽ không phù hợp với nhân vật bổ sung?
Matthew D. Scholefield

1
GNU sed mà không có -rtùy chọn sử dụng cú pháp biểu thức chính quy này .
glenn jackman

0

Từ đoạn mã (bị hỏng) mà bạn đã đăng, dường như bạn cũng muốn thay thế dòng mới. Trong trường hợp đó, regex neo tự nó không thể giúp bạn. Sau đây là một giải pháp:

sed '/[[:alpha:]]\+$/{N;s/[[:alpha:]]\+\n/replace/}' your_file

Hỏng:

  • /[a-zA-Z]\+$/{} có nghĩa là áp dụng bất cứ điều gì đến bên trong các đường cong cho các đường khớp với biểu thức chính quy.
  • Regex là một trong đó sử dụng neo như trong câu trả lời của riêng bạn , được sửa đổi để đưa ý kiến ​​của glenn jackman vào tài khoản.
  • Bên trong các curlies, Ncó nghĩa là "nối dòng tiếp theo vào bộ đệm hoạt động" (cái sedgọi là 'không gian mẫu')
  • Cuối cùng, s///tuyên bố là sự thay thế cần thiết của bạn. Bây giờ nó hoạt động vì không gian mẫu chứa hai dòng liên tiếp và do đó dòng mới là một phần của nó.

0

Để tìm cuối dòng, chỉ cần sử dụng $ -sign :

Không có kết thúc của dòng neo:

sed -n '/pattern/p' file 

Không có kết thúc của dòng neo:

sed -n '/pattern$/p' file
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.