Sed có thể thay thế các nhân vật dòng mới?


42

Có một vấn đề với sed và nhân vật dòng mới?
Tôi có một tệp test.txt với các nội dung sau

aaaaa  
bbbbb  
ccccc  
ddddd  

Những điều sau đây không hoạt động:
sed -r -i 's/\n/,/g' test.txt

Tôi biết rằng tôi có thể sử dụng trcho việc này nhưng câu hỏi của tôi là tại sao dường như không thể với sed.

Nếu đây là tác dụng phụ của việc xử lý từng dòng tệp, tôi sẽ quan tâm đến lý do tại sao điều này xảy ra. Tôi nghĩ greploại bỏ các dòng mới. Liệu sed có làm như vậy không?


1
Trong trường hợp này, sed có thể không phải là công cụ tốt nhất để sử dụng (ví dụ: "tr"). Có những công cụ trực quan hơn, dễ đọc / bảo trì hơn, hoạt động tốt hơn (đặc biệt là trên dữ liệu lớn), v.v ... Đừng sử dụng búa của bạn để đặt ốc vít vào (ngay cả khi nó hoạt động). Bạn có thể tìm thấy một so sánh trên: http://slash4.de/blog/python/sed-replace-newline-or-python-awk-tr-perl-xargs.html
omeater 26/215

2
trsẽ thêm một dấu ,và sẽ tạo ra một dòng bị hủy. Tốt nhất là sử dụng pastethay thế:paste -sd , test.txt
Stéphane Chazelas

Câu trả lời:


48

Với GNU sedvà được cung cấp POSIXLY_CORRECTkhông có trong môi trường (đối với đầu vào một dòng):

sed -i ':a;N;$!ba;s/\n/,/g' test.txt

Từ https://stackoverflow.com/questions/1251999/sed-how-can-i-replace-a-newline-n :

  1. tạo nhãn thông qua :a
  2. nối dòng hiện tại và dòng tiếp theo vào không gian mẫu thông qua N
  3. nếu chúng ta ở trước dòng cuối cùng, hãy phân nhánh đến nhãn đã tạo $!ba( $!có nghĩa là không thực hiện trên dòng cuối cùng (vì sẽ có một dòng mới cuối cùng)).
  4. cuối cùng, sự thay thế thay thế mọi dòng mới bằng dấu phẩy trên không gian mẫu (là toàn bộ tệp).

Điều này dường như chỉ ra rằng vấn đề là sed đọc từng dòng. Nhưng tôi không thể hiểu tại sao đây là vấn đề. Nó chỉ có thể đọc dòng và thay thế ký tự dòng mới (hoặc ký tự cuối cùng) bằng một,
Jim

1
@jim Có vẻ như nó không nằm trong bộ đệm để được khớp, nhưng tôi không rành về sed, có lẽ ai đó có thể làm sáng tỏ điều đó. Tôi nghĩ bạn nên mở rộng Q của mình với thông tin cụ thể đó, để mọi người có nhiều khả năng đọc nó hơn và hy vọng sẽ trả lời.
Anthon

Kết quả này trongba: Event not found
krb686

@ krb686 "Cái này" bạn đang đề cập đến là gì? Bạn đã chạy sedlệnh trên với các tùy chọn chính xác? Trên test.txt tập tin nào? Với phiên bản nào của sed(thử sed --version)?
Anthon

@Anthon Xin lỗi, tôi nghĩ tôi có ý nói "the". Tôi đọc một bài viết SO khác thông báo cho tôi rằng csh yêu cầu tôi phải thoát !. Thật thú vị, điều đó vẫn không hiệu quả với tôi và cuối cùng tôi đã phải thoát gấp đôi !trong .cshkịch bản của mình . Vì vậy, hiện tại tôi không có vấn đề gì, nhưng bạn có biết tại sao điều đó có thể xảy ra không? Những gì làm việc cho tôi làsed :a;N;$\\!ba;s/\n/ /g'
krb686

16

Điều này hoạt động với GNU sed:

sed -z 's/\n/,/g' 

-z được bao gồm từ 4.2.2

Lưu ý -zthay đổi dấu phân cách thành ký tự null ( \0). Nếu đầu vào của bạn không chứa bất kỳ ký tự null nào, toàn bộ đầu vào được coi là một dòng duy nhất. Điều này có thể đi kèm với những hạn chế của nó .

Để tránh thay thế dòng mới của dòng cuối cùng, bạn có thể thay đổi lại dòng sau:

sed -z 's/\n/,/g;s/,$/\n/'

(Đó là sedcú pháp GNU một lần nữa, nhưng nó không thành vấn đề vì toàn bộ chỉ là GNU)


3
Điều này cũng sẽ thay thế dòng mới có thể không phải là điều OP muốn ... so sánh kết quả với giải pháp của mikeerv .
don_crissti

7

Từ trang web của Oracle:

Tiện ích sed hoạt động bằng cách đọc tuần tự một tệp, từng dòng, vào bộ nhớ. Sau đó, nó thực hiện tất cả các hành động được chỉ định cho dòng và đặt dòng trở lại trong bộ nhớ để kết xuất đến thiết bị đầu cuối với các thay đổi được yêu cầu. Sau khi tất cả các hành động đã diễn ra với một dòng này, nó sẽ đọc dòng tiếp theo của tệp và lặp lại quá trình cho đến khi hoàn thành với tệp.

Về cơ bản điều này có nghĩa là vì sed đang đọc từng dòng nên ký tự dòng mới không khớp.

Giải pháp từ https://stackoverflow.com/questions/1251999/sed-how-can-i-replace-a-newline-n là:

sed ':a;N;$!ba;s/\n/,/g'

hoặc, trong phiên bản di động (không được ;nối sau nhãn dấu nhảy)

sed -e ':a' -e 'N;$!ba' -e 's/\n/,/g'

Một lời giải thích về cách thức hoạt động được cung cấp trên trang đó.


Tôi đã sử dụng một hình thức sửa đổi này để phân tích các bản ghi VPN và đặt thông tin về dấu thời gian và xác thực của người dùng trên cùng một dòng. Chúc mừng!
dùng208145

Lưu ý rằng cú pháp đó là cụ thể của GNU và ngay cả với GNU sed, nếu POSIXLY_CORRECT ở trong môi trường và đầu vào chỉ có một dòng, sẽ không có đầu ra.
Stéphane Chazelas

5

sedluôn loại bỏ \newline trailing ngay trước khi điền vào không gian mẫu và sau đó nối thêm một trước khi viết ra kết quả của tập lệnh. Một \newline có thể có trong không gian mẫu bằng nhiều cách khác nhau - nhưng không bao giờ nếu đó không phải là kết quả của chỉnh sửa. Điều này rất quan trọng - \newlines trong sedkhông gian mẫu luôn phản ánh sự thay đổi và không bao giờ xảy ra trong luồng đầu vào. \newlines là dấu phân cách duy nhất mà một sedder có thể dựa vào với đầu vào không xác định.

Nếu bạn muốn thay thế tất cả các \newlines bằng dấu phẩy và tệp của bạn không lớn lắm, thì bạn có thể làm:

sed 'H;1h;$!d;x;y/\n/,/'

Điều đó nối thêm mọi dòng đầu vào vào hkhông gian cũ - ngoại trừ dòng đầu tiên, thay vào đó sẽ ghi đè lên hkhông gian cũ - theo sau một \nký tự ewline. Sau đó, nó dbỏ qua mọi dòng không phải là $!cuối cùng từ đầu ra. Trên dòng cuối cùng H, không gian cũ và mẫu được xthay đổi và tất cả các \nký tự ewline được y///dịch sang dấu phẩy.

Đối với các tệp lớn, loại điều này bị ràng buộc gây ra sự cố - sedbộ đệm trên các ranh giới dòng, có thể dễ dàng bị tràn qua các hành động thuộc loại này.


2

Ngoài ra, bạn có thể sử dụng cú pháp đơn giản hơn một chút:

sed ':a;N;s/\n/,/g;ba'

... Chỉ cần thay đổi thứ tự trình tự.


3
Nhưng chạy slệnh cho mỗi dòng đầu vào trên một không gian mẫu ngày càng lớn.
Stéphane Chazelas

1

Có một số ma thuật sed rất đẹp ở đây. Và một số điểm tốt nêu lên về tràn không gian mô hình. Tôi thích sử dụng sed ngay cả khi nó không phải là cách đơn giản nhất, vì nó rất nhỏ gọn và mạnh mẽ. Tuy nhiên, nó có những hạn chế và đối với một lượng lớn dữ liệu, không gian mẫu sẽ phải là mahoosive.

GNU nói điều này:

Đối với những người muốn viết các tập lệnh sed di động, hãy lưu ý rằng một số triển khai đã được biết là giới hạn độ dài dòng (đối với mẫu và khoảng trắng giữ) không quá 4000 byte. Tiêu chuẩn posix chỉ định rằng việc triển khai sed phù hợp sẽ hỗ trợ ít nhất 8192 byte độ dài dòng. GNU sed không có giới hạn tích hợp về độ dài dòng; miễn là nó có thể malloc () thêm bộ nhớ (ảo), bạn có thể cung cấp hoặc xây dựng các dòng miễn là bạn muốn.
Tuy nhiên, đệ quy được sử dụng để xử lý các mẫu con và sự lặp lại không xác định. Điều này có nghĩa là không gian ngăn xếp có sẵn có thể giới hạn kích thước của bộ đệm có thể được xử lý theo các mẫu nhất định.

Tôi không có nhiều thứ để thêm, nhưng tôi muốn hướng bạn đến hướng dẫn đi của tôi cho sed . Nó là tuyệt vời. http://www.grymoire.com/Unix/Sed.html

và đây là giải pháp của tôi:

for i in $(cat test.txt); do echo -n $i','; done; echo '' >> somewhere

nó hoạt động tốt



-1

Giả sử bạn muốn thay thế dòng mới bằng \n. Tôi muốn làm điều đó, vì vậy đây là những gì tôi đã làm:

(echo foo; echo bar; echo baz) | sed -r '$!s/$/\\n/' | tr -d '\n' 
# Output: foo\nbar\nbaz

Đây là những gì nó làm: cho tất cả các dòng ngoại trừ cuối cùng , nối thêm \n. Sau đó, xóa dòng mới với tr.


-rchỉ có sẵn trong GNU sed, không phải BSD.
kenorb
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.