Làm cách nào tôi có thể thay thế một dòng mới (\ n) bằng sed?


1371

Làm cách nào tôi có thể thay thế một dòng mới ("\n ") bằng khoảng trắng (" ") bằng sedlệnh?

Tôi đã thử không thành công:

sed 's#\n# #g' file
sed 's#^$# #g' file

Làm thế nào để tôi sửa nó?


27
trchỉ là công cụ phù hợp cho công việc nếu thay thế một ký tự cho một ký tự, trong khi ví dụ trên hiển thị thay thế dòng mới bằng một khoảng trắng .. Vì vậy, trong ví dụ trên, tr có thể hoạt động .. Nhưng sẽ hạn chế về sau.
Tức giận 84

9
trtrong công cụ phù hợp cho công việc vì người hỏi muốn thay thế mỗi dòng mới bằng một khoảng trắng, như trong ví dụ của anh ta. Việc thay thế các dòng mới là phức tạp nhất sednhưng dễ dàng thực hiện tr. Đây là một câu hỏi phổ biến. Việc thực hiện thay thế regex không được thực hiện bởi trnhưng sedđó sẽ là công cụ phù hợp ... cho một câu hỏi khác.
Mike S

3
"tr" cũng có thể xóa dòng mới `tr -d '\ n'` tuy nhiên bạn cũng có thể muốn xóa trả về để phổ quát hơn `tr -d '\ 012 \ 015'`.
anthony

2
CẢNH BÁO: "tr" hoạt động khác nhau liên quan đến phạm vi ký tự giữa Linux và các máy Solaris cũ hơn (EG sol5.8). EG: `tr -d 'az'` và` tr -d '[az]' `. Vì vậy, tôi khuyên bạn nên sử dụng "sed" không có sự khác biệt đó.
anthony

2
@MikeS Cảm ơn câu trả lời. Thực hiện theo tr '\012' ' 'một echo. Nếu không, linefeed cuối cùng trong tệp cũng bị xóa. tr '\012' ' ' < filename; echokhông lừa
Bernie Reiter

Câu trả lời:


1514

Sử dụng giải pháp này với GNU sed:

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

Điều này sẽ đọc toàn bộ tệp trong một vòng lặp, sau đó thay thế (các) dòng mới bằng một khoảng trắng.

Giải trình:

  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 một khoảng trắng trên không gian mẫu (là toàn bộ tệp).

Đây là cú pháp tương thích đa nền tảng hoạt động với BSD và OS X sed(theo nhận xét @Benjie ):

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

Như bạn có thể thấy, sử dụng sedcho vấn đề đơn giản này là vấn đề. Đối với một giải pháp đơn giản và đầy đủ xem câu trả lời này .


45
@Arjan và Masi: OS X sử dụng BSD sedthay vì GNU sed, do đó, có thể có một số khác biệt tinh tế (và một số không quá tinh tế) trong cả hai. Đây là một nỗi đau liên tục nếu bạn làm việc trên cả hai máy OS X và * nix. Tôi thường cài đặt GNU coreutilsfindutilstrên OS X và bỏ qua các phiên bản BSD.
Telemachus

50
Các :akhông phải là một thanh ghi, đó là một nhãn chi nhánh. Đó là mục tiêu cho blệnh * hoạt động như "goto". Gọi nó là một thanh ghi ngụ ý rằng bạn có thể tạo các vị trí lưu trữ. Chỉ có hai "thanh ghi"; một cái được gọi là "không gian giữ" mà tập lệnh của bạn không sử dụng và cái còn lại được gọi là "không gian mẫu". Các Nlệnh gắn thêm một dòng mới và và dòng tiếp theo của tập tin đầu vào cho các không gian mẫu. [* Bạn có thể có nhiều nhãn & blệnh. Nếu bạn có một blệnh mà không có nhãn char được gắn vào nó, nó sẽ phân nhánh đến cuối tập lệnh để đọc dòng tiếp theo và lặp lại.]
Tạm dừng cho đến khi có thông báo mới.

108
Bạn có thể chạy đa nền tảng này (tức là trên Mac OS X) bằng cách thực hiện riêng các lệnh thay vì phân tách bằng dấu chấm phẩy: sed -e ':a' -e 'N' -e '$!ba' -e 's/\n/ /g'
Benjie

74
Tại sao không ai bình luận về những gì một mớ hỗn độn này (không phải là câu trả lời, nhưng chương trình mà câu trả lời được đề xuất là giải pháp tốt nhất cho một vấn đề rất đơn giản). Sed trông giống như một chiếc xe thường chạy tốt, nhưng nếu bạn muốn lái xe đến một con phố cụ thể gần đó, cách duy nhất là nâng chiếc xe bằng trực thăng.
Ark-kun

12
Thôi nào mọi người - 261 người ủng hộ cho một giải pháp điên rồ, khó hiểu mà không hiệu quả ???? sed là một công cụ tuyệt vời cho các phần phụ đơn giản trên một dòng, cho bất cứ điều gì khác chỉ cần sử dụng awk. Thật đau buồn ....
Ed Morton

1711

sedđược dự định sẽ được sử dụng trên đầu vào dựa trên dòng. Mặc dù nó có thể làm những gì bạn cần.


Một lựa chọn tốt hơn ở đây là sử dụng trlệnh như sau:

tr '\n' ' ' < input_filename

hoặc xóa hoàn toàn các ký tự dòng mới:

tr -d '\n' < input.txt > output.txt

hoặc nếu bạn có phiên bản GNU (với các tùy chọn dài)

tr --delete '\n' < input.txt > output.txt

88
Sed là dựa trên dòng do đó rất khó để nắm bắt các dòng mới.
Alexander Gladysh

191
sed hoạt động trên một "luồng" đầu vào, nhưng nó hiểu nó trong các đoạn giới hạn dòng mới. Nó là một công cụ unix, có nghĩa là nó làm một việc rất tốt. Một điều là "làm việc trên một dòng tập tin". Làm cho nó làm một cái gì đó khác sẽ khó khăn, và có nguy cơ bị lỗi. Đạo đức của câu chuyện là: chọn đúng công cụ. Rất nhiều câu hỏi của bạn dường như có dạng "Làm thế nào tôi có thể khiến công cụ này làm điều gì đó mà nó không bao giờ phải làm?" Những câu hỏi đó rất thú vị, nhưng nếu chúng xuất hiện trong quá trình giải quyết một vấn đề thực sự, có lẽ bạn đã làm sai.
dmckee --- ex-moderator mèo con

7
@JBBrown trlà một viên đá quý bị bỏ qua để xây dựng đường ống.
dmckee --- ex-moderator mèo con

70
tr là tuyệt vời, nhưng bạn chỉ có thể thay thế dòng mới bằng các ký tự đơn. Bạn cần sử dụng một công cụ khác nếu bạn muốn thay thế các dòng mới bằng một chuỗi
Eddy

21
@Eddy - Tôi đã sử dụng tr để thay thế các dòng mới bằng một ký tự không xuất hiện trong văn bản (tôi đã sử dụng backtick), sau đó sed để thay thế backtick bằng chuỗi tôi muốn sử dụng
rjohnston

494

Trả lời nhanh

sed ':a;N;$!ba;s/\n/ /g' file
  1. : a tạo nhãn 'a'
  2. N nối dòng tiếp theo vào không gian mẫu
  3. $! nếu không phải là dòng cuối cùng , ba nhánh (đi đến) nhãn 'a'
  4. s thay thế , / \ n / regex cho dòng mới , / / bởi một khoảng trắng , / g khớp toàn cục (càng nhiều lần càng tốt)

sed sẽ lặp qua bước 1 đến 3 cho đến khi đến dòng cuối cùng, nhận được tất cả các dòng vừa với không gian mẫu trong đó sed sẽ thay thế tất cả các ký tự \ n


Lựa chọn thay thế

Tất cả các lựa chọn thay thế, không giống như sed sẽ không cần phải đến dòng cuối cùng để bắt đầu quá trình

với bash , chậm

while read line; do printf "%s" "$line "; done < file

với perl , sed -like tốc độ

perl -p -e 's/\n/ /' file

với tr , nhanh hơn sed , chỉ có thể thay thế bằng một ký tự

tr '\n' ' ' < file

với dán , tốc độ giống như tr , chỉ có thể thay thế bằng một ký tự

paste -s -d ' ' file

với awk , tốc độ như tr

awk 1 ORS=' ' file

Thay thế khác như "echo $ (<file)" là chậm, chỉ hoạt động trên các tệp nhỏ và cần xử lý toàn bộ tệp để bắt đầu quá trình.


Câu trả lời dài từ sed FAQ 5.10

5.10. Tại sao tôi không thể khớp hoặc xóa một dòng mới bằng cách sử dụng
chuỗi thoát \ n ? Tại sao tôi không thể kết hợp 2 dòng trở lên bằng \ n?

\ N sẽ không bao giờ khớp với dòng mới ở cuối dòng vì
dòng mới luôn bị loại bỏ trước khi dòng được đặt vào
không gian mẫu. Để có được 2 hoặc nhiều dòng vào không gian mẫu, hãy sử dụng
lệnh 'N' hoặc một cái gì đó tương tự (chẳng hạn như 'H; ...; g;').

Sed hoạt động như thế này: sed đọc từng dòng một, cắt
bỏ dòng mới kết thúc, đặt những gì còn lại vào không gian mẫu nơi
tập lệnh sed có thể xử lý hoặc thay đổi nó, và khi không gian mẫu
được in, sẽ thêm một dòng mới vào thiết bị xuất chuẩn (hoặc vào một tập tin). Nếu
không gian mẫu bị xóa hoàn toàn hoặc một phần với 'd' hoặc 'D',
dòng mới sẽ không được thêm vào trong các trường hợp như vậy. Do đó, các kịch bản như

  sed 's/\n//' file       # to delete newlines from each line             
  sed 's/\n/foo\n/' file  # to add a word to the end of each line         

sẽ KHÔNG BAO GIỜ hoạt động, bởi vì dòng mới theo dõi được loại bỏ trước khi
dòng được đưa vào không gian mẫu. Để thực hiện các tác vụ trên,
thay vào đó hãy sử dụng một trong các tập lệnh sau:

  tr -d '\n' < file              # use tr to delete newlines              
  sed ':a;N;$!ba;s/\n//g' file   # GNU sed to delete newlines             
  sed 's/$/ foo/' file           # add "foo" to end of each line          

Do các phiên bản của sed khác với GNU sed có giới hạn về kích thước của
bộ đệm mẫu, nên tiện ích 'tr' của Unix được ưu tiên ở đây.
Nếu dòng cuối cùng của tệp chứa một dòng mới, GNU sed sẽ thêm
dòng mới đó vào đầu ra nhưng xóa tất cả các dòng khác, trong khi tr sẽ
xóa tất cả các dòng mới.

Để khớp một khối gồm hai hoặc nhiều dòng, có 3 lựa chọn cơ bản:
(1) sử dụng lệnh 'N' để thêm dòng Tiếp theo vào không gian mẫu;
(2) sử dụng lệnh 'H' ít nhất hai lần để nối dòng hiện tại
vào không gian Giữ, sau đó truy xuất các dòng từ không gian giữ
bằng x, g hoặc G; hoặc (3) sử dụng phạm vi địa chỉ (xem phần 3.3, ở trên)
để khớp các dòng giữa hai địa chỉ được chỉ định.

Các lựa chọn (1) và (2) sẽ đặt \ n vào không gian mẫu, tại đó nó
có thể được xử lý theo ý muốn ('s / ABC \ nXYZ / alph / g'). Một ví dụ
về việc sử dụng 'N' để xóa một khối các dòng xuất hiện trong phần 4.13
("Làm cách nào để xóa một khối các dòng liên tiếp cụ thể ?").
Ví dụ này có thể được sửa đổi bằng cách thay đổi lệnh xóa thành một thứ
khác, như 'p' (in), 'i' (insert), 'c' (thay đổi), 'a' (chắp thêm)
hoặc 's' (thay thế) .

Choice (3) sẽ không chấm \ n vào không gian mô hình, nhưng nó không
phù hợp với một khối dòng liên tiếp, vì vậy nó có thể là bạn không
còn cần \ n để tìm thấy những gì bạn đang tìm kiếm. Do GNU sed
phiên bản 3.02.80 hiện hỗ trợ cú pháp này:

  sed '/start/,+4d'  # to delete "start" plus the next 4 lines,           

ngoài các
địa chỉ phạm vi '/ từ đây /, / đến đó / {...}' truyền thống , có thể tránh hoàn toàn việc sử dụng \ n.


6
trlà một ý tưởng tuyệt vời và phạm vi bảo hiểm tổng thể của bạn tạo nên một câu trả lời chất lượng hàng đầu.
New Alexandria

1
+1 để sử dụng ( tiện ích tiêu chuẩn ) paste... và tất cả những thứ khác!
Totor 15/03/13


4
Phần tốt nhất về câu trả lời này là "câu trả lời dài" giải thích chính xác cách thức và lý do tại sao lệnh hoạt động.
pdwalker

3
Đây có thể là hữu ích nhất trong số hàng ngàn câu trả lời tôi đã đọc trên stackexchange. Tôi cần phải khớp nhiều ký tự trên các dòng. Không có ví dụ sed nào trước đây bao gồm nhiều dòng và tr không thể xử lý khớp nhiều ký tự. Perl có vẻ tốt, nhưng không hoạt động như tôi mong đợi. Tôi sẽ bỏ phiếu cho câu trả lời này nhiều lần nếu tôi có thể.
mightypile

225

Một thay thế awk ngắn hơn:

awk 1 ORS=' '

Giải trình

Một chương trình awk được xây dựng từ các quy tắc bao gồm các khối mã có điều kiện, nghĩa là:

condition { code-block }

Nếu khối mã bị bỏ qua, mặc định được sử dụng : { print $0 }. Do đó, điều 1này được hiểu là một điều kiện thực sự và print $0được thực thi cho mỗi dòng.

Khi awkđọc đầu vào, nó sẽ phân tách nó thành các bản ghi dựa trên giá trị của RS(Dấu tách bản ghi), theo mặc định là một dòng mới, do đó awktheo mặc định sẽ phân tích cú pháp dòng đầu vào. Việc chia tách cũng liên quan đến việc tước khỏi RSbản ghi đầu vào.

Bây giờ, khi in một bản ghi, ORS(Dấu tách bản ghi đầu ra) được thêm vào nó, mặc định lại là một dòng mới. Vì vậy, bằng cách thay đổi ORSthành một không gian, tất cả các dòng mới được thay đổi thành không gian.


5
Tôi thích rất nhiều giải pháp đơn giản này, dễ đọc hơn nhiều so với các giải pháp khác
Fedir RYKHTIK

8
Nếu nó có ý nghĩa hơn, điều này có thể được viết một cách hiệu quả như sau: awk 'BEGIN { ORS=" " } { print $0 } END { print "\n"} ' file.txt(thêm một dòng mới kết thúc chỉ để minh họa bắt đầu / kết thúc); "1" ước tính true(xử lý dòng) và print(in dòng). Một điều kiện cũng có thể được thêm vào biểu thức này, ví dụ: chỉ hoạt động trên các dòng khớp với một mẫu: awk 'BEGIN { ORS=" " } /pattern/ { print $0 } END { print "\n"} '
michael

2
Bạn có thể làm điều đó nhiều hơn simle: codeawk 'ORS = ""' file.txtcode
Udi

Khi sử dụng awk như thế này, thật không may, nguồn cấp dữ liệu cuối cùng trong tệp cũng bị xóa. Xem Patrick Dark trả lời ở trên về việc sử dụng 'tr' trong một lớp con như `tệp mèo | echo $ (tr "\ 012" "") `thực hiện thủ thuật. Tiện lợi
Bernie Reiter

143

gnu sed có một tùy chọn -zcho các bản ghi (dòng) null. Bạn chỉ có thể gọi:

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

4
Ngay cả khi đầu vào không chứa null, chúng sẽ được giữ nguyên (dưới dạng các dấu phân cách bản ghi).
Toby Speight

6
Điều này sẽ không tải toàn bộ đầu vào nếu không có null? Trong trường hợp này, việc xử lý một tệp nhiều gigabyte có thể bị hỏng.
Ruslan

3
@Ruslan, vâng, nó tải toàn bộ đầu vào. Giải pháp này không phải là một ý tưởng tốt cho các tệp nhiều gigabyte.
JJoao

7
Đây thực sự là câu trả lời tốt nhất . Các biểu thức khác là quá méo để nhớ. @JJoao Bạn có thể sử dụng nó với -u, --unbuffered. Pháp mansư nói: "tải lượng dữ liệu tối thiểu từ các tệp đầu vào và xả bộ đệm đầu ra thường xuyên hơn".
not2qubit

vì thế. nhiều điều này.
sjas

85

Các Perl phiên bản hoạt động theo cách bạn mong đợi.

perl -i -p -e 's/\n//' file

Như đã chỉ ra trong các bình luận, đáng chú ý là điều này chỉnh sửa tại chỗ. -i.baksẽ cung cấp cho bạn bản sao lưu của tệp gốc trước khi thay thế trong trường hợp biểu thức thông thường của bạn không thông minh như bạn nghĩ.


23
Xin vui lòng ít nhất đề cập rằng -ikhông có hậu tố làm cho không có bản sao lưu . -i.bakbảo vệ bạn khỏi một sai lầm dễ dãi, xấu xí (nói, quên gõ -pvà bỏ tập tin).
Telemachus

6
@Telemachus: Đó là một điểm công bằng, nhưng nó có thể được tranh luận theo bất kỳ cách nào. Lý do chính tôi không đề cập đến là vì ví dụ sed trong câu hỏi của OP không tạo bản sao lưu, vì vậy có vẻ như không cần thiết ở đây. Lý do khác là tôi chưa bao giờ thực sự sử dụng chức năng sao lưu (thực sự tôi thấy sao lưu tự động gây phiền nhiễu), vì vậy tôi luôn quên nó ở đó. Lý do thứ ba là nó làm cho dòng lệnh của tôi dài hơn bốn ký tự. Dù tốt hay xấu (có thể tệ hơn), tôi là một người tối giản bắt buộc; Tôi chỉ thích sự ngắn gọn. Tôi nhận ra bạn không đồng ý. Tôi sẽ cố gắng hết sức để nhớ để cảnh báo về các bản sao lưu trong tương lai.
ire_and_curses

6
@Ire_and_curses: Thật ra, bạn vừa đưa ra một lập luận tốt chết tiệt vì đã bỏ qua tôi. Đó là, bạn có lý do cho lựa chọn của mình, và liệu tôi có đồng ý với các lựa chọn đó hay không, tôi chắc chắn tôn trọng điều đó. Tôi không chắc chắn hoàn toàn tại sao, nhưng gần đây tôi đã rơi nước mắt về điều đặc biệt này ( -icờ trong Perl không có hậu tố). Tôi chắc chắn tôi sẽ tìm thấy một cái gì đó khác để ám ảnh về sớm. :)
Telemachus

Thật đáng tiếc rằng điều này không hoạt động với stdin bằng cách chỉ định -tên tệp. Có cách nào làm được việc này không? Đó là cách để tôi không lo lắng về việc sửa đổi tệp đang sử dụng một đường ống bắt đầu bằng con mèo.
Steven Lu

@StevenLu Perl sẽ đọc từ STDIN theo mặc định nếu không có tên tệp nào được cung cấp. Vì vậy, bạn có thể làm ví dụperl -i -p -e 's/\n//' < infile > outfile
ire_and_curses

44

Ai cần sed? Đây là bashcách:

cat test.txt |  while read line; do echo -n "$line "; done

2
Upvote, tôi thường sử dụng câu trả lời hàng đầu, nhưng khi đường ống / dev / urandom qua nó, sed sẽ không in cho đến khi EOF và ^ C không phải là EOF. Giải pháp này in mỗi khi nó nhìn thấy một dòng mới. Chính xác những gì tôi cần! Cảm ơn!
Vasiliy Sharapov

1
sau đó tại sao không: echo -n `cat days.txt` Từ bài đăng này
Tony

9
@Tony vì backticks không được dùng nữa và con mèo là dư thừa ;-) Sử dụng: echo $ (<days.txt)
seumasmac

10
Mà không cần sử dụng cat: while read line; do echo -n "$line "; done < test.txt. Có thể hữu ích nếu một vỏ phụ là một vấn đề.
Carlo Cannas

5
echo $(<file)siết chặt tất cả các khoảng trắng vào một không gian duy nhất, không chỉ là dòng mới: điều này vượt xa những gì OP đang yêu cầu.
glenn jackman

27

Để thay thế tất cả các dòng mới bằng khoảng trắng bằng awk, mà không cần đọc toàn bộ tệp vào bộ nhớ:

awk '{printf "%s ", $0}' inputfile

Nếu bạn muốn một dòng mới cuối cùng:

awk '{printf "%s ", $0} END {printf "\n"}' inputfile

Bạn có thể sử dụng một ký tự khác ngoài không gian:

awk '{printf "%s|", $0} END {printf "\n"}' inputfile

END{ print ""}là một thay thế ngắn hơn cho một dòng mới.
Isaac

22
tr '\n' ' ' 

là mệnh lệnh.

Đơn giản và dễ sử dụng.


14
hoặc đơn giản là tr -d '\n'nếu bạn không muốn thêm một khoảng
trắng

21

Ba thứ.

  1. tr(hoặc cat, v.v.) là hoàn toàn không cần thiết. (GNU) sedvà (GNU) awk, khi được kết hợp, có thể thực hiện 99,9% bất kỳ xử lý văn bản nào bạn cần.

  2. stream! = dòng dựa. edlà một biên tập viên dựa trên dòng. sedkhông phải. Xem bài giảng sed để biết thêm thông tin về sự khác biệt. Hầu hết mọi người nhầm lẫn sedlà dựa trên dòng vì theo mặc định, nó không tham lam trong việc khớp mẫu của nó cho các kết quả khớp SIMPLE - ví dụ, khi thực hiện tìm kiếm mẫu và thay thế bằng một hoặc hai ký tự, theo mặc định, nó chỉ thay thế cho kết quả khớp đầu tiên nó tìm thấy (trừ khi có quy định khác bởi lệnh toàn cầu). Thậm chí sẽ không có lệnh toàn cầu nếu nó dựa trên dòng thay vì dựa trên STREAM, bởi vì nó sẽ chỉ đánh giá các dòng tại một thời điểm. Hãy thử chạy ed; bạn sẽ nhận thấy sự khác biệt edlà khá hữu ích nếu bạn muốn lặp lại các dòng cụ thể (chẳng hạn như trong một vòng lặp for), nhưng hầu hết các lần bạn sẽ chỉ muốn sed.

  3. Điều đó đang được nói,

    sed -e '{:q;N;s/\n/ /g;t q}' file
    

    chỉ hoạt động tốt trong sedphiên bản GNU 4.2.1. Lệnh trên sẽ thay thế tất cả các dòng mới bằng dấu cách. Nó xấu và hơi cồng kềnh khi gõ, nhưng nó hoạt động tốt. Có {}thể bỏ qua, vì chúng chỉ được bao gồm vì lý do tỉnh táo.


3
Là một người chỉ biết đủ sedđể làm những thứ cơ bản, tôi phải nói nó nhiều hơn những gì bạn có thể làm với sedmà là dễ hiểu những gì đang diễn ra. Tôi có một thời gian rất khó khăn để làm việc với sedvì vậy tôi thích một lệnh đơn giản hơn khi tôi có thể sử dụng nó.
Nate

Sử dụng t qnhư bước nhảy có điều kiện, nó hoạt động với một mẫu như s/\n / /(để nối tất cả các dòng bắt đầu bằng khoảng trắng) mà không cần đọc toàn bộ tệp vào bộ nhớ. Tiện dụng khi chuyển đổi tập tin nhiều megabyte.
texthell

Bài viết bạn đã liên kết không phản ánh những gì bạn đang nói
hek2mgl

Điều này chậm hơn gần 800 lần so với câu trả lời được chấp nhận trên đầu vào lớn. Điều này là do chạy thay thế cho mỗi dòng trên đầu vào ngày càng lớn hơn.
Thor

13

Câu trả lời với: một nhãn ...

Làm cách nào tôi có thể thay thế một dòng mới (\ n) bằng sed?

... không hoạt động trong freebsd 7.2 trên dòng lệnh:

(echo foo; thanh echo) | sed ': a; N; $! ba; s / \ n / / g'
sed: 1: ": a; N; $! ba; s / \ n / / g": nhãn không sử dụng 'a; N; $! ba; s / \ n / / g'
foo
quán ba

Nhưng nếu bạn đặt tập lệnh sed vào một tập tin hoặc sử dụng -e để "xây dựng" tập lệnh sed ...

> (echo foo; echo bar) | sed -e: a -e N -e '$! ba' -e 's / \ n / / g'
thanh foo

hoặc là ...

> cat > x.sed << eof
:a
N
$!ba
s/\n/ /g
eof

> (echo foo; echo bar) | sed -f x.sed
foo bar

Có lẽ sed trong OS X cũng tương tự.


Một loạt các đối số -e làm việc cho tôi trên windows bằng MKS! Cảm ơn!
JamesG

12

Giải pháp dễ hiểu

Tôi đã có vấn đề này. Yếu tố chính là tôi cần giải pháp để hoạt động trên BSD (Mac OS X) và GNU's (Linux và Cygwin ) sedtr:

$ echo 'foo
bar
baz


foo2
bar2
baz2' \
| tr '\n' '\000' \
| sed 's:\x00\x00.*:\n:g' \
| tr '\000' '\n'

Đầu ra:

foo
bar
baz

(có dòng mới)

Nó hoạt động trên Linux, OS X và BSD - ngay cả khi không có hỗ trợ UTF-8 hoặc với thiết bị đầu cuối tào lao.

  1. Sử dụng trđể trao đổi dòng mới với một nhân vật khác.

    NULL( \000hoặc \x00) là tốt vì nó không cần hỗ trợ UTF-8 và nó không có khả năng được sử dụng.

  2. Sử dụng sedđể phù hợp vớiNULL

  3. Sử dụng trđể trao đổi lại các dòng mới nếu bạn cần chúng


1
Một lưu ý tinh tế về danh pháp: ký tự \000thường được gọi là NUL(một L) và NULLthường được sử dụng khi nói về một con trỏ zero (trong C / C ++).
sqweek


9

Tôi không phải là chuyên gia, nhưng tôi đoán sedtrước tiên bạn cần nối dòng tiếp theo vào không gian mẫu, sử dụng " N". Từ phần "Không gian mô hình đa dòng" trong "Advanced sed Commands" của cuốn sách sed & awk (Dale Dougherty và Arnold Robbins; O'Reilly 1997; trang 107 trong bản xem trước ):

Lệnh multiline Next (N) tạo ra một không gian mẫu multiline bằng cách đọc một dòng đầu vào mới và nối nó vào nội dung của không gian mẫu. Nội dung ban đầu của không gian mẫu và dòng đầu vào mới được phân tách bằng một dòng mới. Ký tự dòng mới được nhúng có thể được khớp trong các mẫu theo trình tự thoát "\ n". Trong không gian mẫu đa dòng, metacharacter "^" khớp với ký tự đầu tiên của không gian mẫu và không phải (các) ký tự theo sau bất kỳ dòng mới được nhúng nào. Tương tự, "$" chỉ khớp với dòng mới cuối cùng trong không gian mẫu và không phải bất kỳ dòng mới nào được nhúng. Sau khi lệnh Tiếp theo được thực thi, điều khiển sẽ được chuyển cho các lệnh tiếp theo trong tập lệnh.

Từ man sed:

[2addr] N

Nối dòng đầu vào tiếp theo vào không gian mẫu, sử dụng ký tự dòng mới được nhúng để tách tài liệu được nối với nội dung gốc. Lưu ý rằng số dòng hiện tại thay đổi.

Tôi đã sử dụng điều này để tìm kiếm (nhiều) tệp nhật ký được định dạng sai, trong đó chuỗi tìm kiếm có thể được tìm thấy trên dòng tiếp theo "mồ côi".


7

Tôi đã sử dụng một phương pháp lai để khắc phục điều mới bằng cách sử dụng tr để thay thế dòng mới bằng các tab, sau đó thay thế các tab bằng bất cứ thứ gì tôi muốn. Trong trường hợp này, "
" vì tôi đang cố gắng tạo các ngắt HTML.

echo -e "a\nb\nc\n" |tr '\n' '\t' | sed 's/\t/ <br> /g'`

6

Để đáp ứng với giải pháp "tr" ở trên, trên Windows (có thể sử dụng phiên bản tr của Gnuwin32), giải pháp được đề xuất:

tr '\n' ' ' < input

không hoạt động với tôi, nó sẽ bị lỗi hoặc thực sự thay thế \ nw / '' vì một số lý do.

Sử dụng một tính năng khác của tr, tùy chọn "xóa" -d đã hoạt động:

tr -d '\n' < input

hoặc '\ r \ n' thay vì '\ n'


3
Trên Windows, có lẽ bạn cần sử dụng tr "\n" " " < input. Shell Windows (cmd.exe) không coi dấu nháy đơn là ký tự trích dẫn.
Keith Thompson

Không, trong hệ thống con Ubuntu của Windows 10, bạn cần sử dụngtr "\n\r" " " < input.txt > output.txt
user1491819

Điều này hoạt động trên Windows 10 bằng Gnuwin32 : cat SourceFile.txt | tr --delete '\r\n' > OutputFile.txt. Hoặc, thay vì GnuWin32, sử dụng Gow (Gnu trên Windows), github.com/bmatzelle/gow/wiki
Alchemistmatt

5

Giải pháp chống đạn. Nhị phân dữ liệu an toàn và tuân thủ POSIX, nhưng chậm.

POSIX sed yêu cầu đầu vào theo tệp văn bản POSIX POSIXdòng POSIX định nghĩa , do đó, NULL-byte và quá dài không được phép và mỗi dòng phải kết thúc bằng một dòng mới (bao gồm cả dòng cuối cùng). Điều này làm cho nó khó sử dụng sed để xử lý dữ liệu đầu vào tùy ý.

Giải pháp sau đây tránh sed và thay vào đó chuyển đổi các byte đầu vào thành mã bát phân rồi lại thành byte, nhưng chặn mã bát phân 012 (dòng mới) và đưa ra chuỗi thay thế thay cho nó. Theo như tôi có thể nói giải pháp là tuân thủ POSIX, vì vậy nó nên hoạt động trên nhiều nền tảng khác nhau.

od -A n -t o1 -v | tr ' \t' '\n\n' | grep . |
  while read x; do [ "0$x" -eq 012 ] && printf '<br>\n' || printf "\\$x"; done

Tài liệu tham khảo POSIX: sh , ngôn ngữ lệnh shell , od , tr , grep , đọc , [ , printf .

cả hai read,[printfđược tích hợp ít nhất là bash, nhưng điều đó có thể không được đảm bảo bởi POSIX, vì vậy trên một số nền tảng, có thể mỗi byte đầu vào sẽ bắt đầu một hoặc nhiều quy trình mới, điều này sẽ làm mọi thứ chậm lại. Ngay cả trong bash, giải pháp này chỉ đạt khoảng 50 kB / s, vì vậy nó không phù hợp với các tệp lớn.

Đã thử nghiệm trên Ubuntu (bash, dash và busybox), FreeBSD và OpenBSD.


5

Trong một số tình huống có thể bạn có thể thay đổi RSthành một số chuỗi hoặc ký tự khác. Bằng cách này, \ n có sẵn cho phụ / gsub:

$ gawk 'BEGIN {RS="dn" } {gsub("\n"," ") ;print $0 }' file

Sức mạnh của kịch bản shell là nếu bạn không biết cách thực hiện theo một cách khác, bạn có thể thực hiện theo cách khác. Và nhiều khi bạn có nhiều điều cần tính đến hơn là đưa ra một giải pháp phức tạp cho một vấn đề đơn giản.

Về điều mà gawk chậm ... và đọc tệp vào bộ nhớ, tôi không biết điều này, nhưng đối với tôi, gawk dường như hoạt động với một dòng tại thời điểm đó và rất nhanh (không nhanh như một số người khác , nhưng thời gian để viết và kiểm tra cũng được tính).

Tôi xử lý MB và thậm chí GB dữ liệu và giới hạn duy nhất tôi tìm thấy là kích thước dòng.


5

Nếu bạn không may phải xử lý các kết thúc dòng windows, bạn cần xóa \r\n

tr '[\r\n]' ' ' < $input > $output

Điều này thay thế [bằng một không gian, và \rvới một không gian, và \nvới một không gian, và ]với một không gian. tr -d '\r\n' <filesẽ loại bỏ bất kỳ \rhoặc \nký tự, nhưng đó cũng không phải là những gì đang được yêu cầu. tr -d '\r' <filesẽ xóa bất kỳ \rký tự nào (bất kể chúng có liền kề hay không \n) có lẽ gần với tính hữu dụng cũng như hoàn toàn có thể đúng với nhu cầu của OP (vẫn cho rằng bạn trhiểu ký hiệu dấu gạch chéo ngược này).
tripleee

4

Bạn có thể sử dụng xargs- nó sẽ thay thế \nbằng một khoảng trắng theo mặc định.

Tuy nhiên, nó sẽ có vấn đề nếu đầu vào của bạn có bất kỳ trường hợp nào unterminated quote, ví dụ: nếu dấu ngoặc kép trên một dòng nhất định không khớp.


xargs cũng xử lý dòng cuối cùng độc đáo:
AAAfarmclub

4

Tìm và thay thế bằng cách sử dụng cho phép \ n

sed -ie -z 's/Marker\n/# Marker Comment\nMarker\n/g' myfile.txt

Đánh dấu

Trở thành

# Nhận xét đánh dấu

Đánh dấu


4

Tại sao tôi không tìm thấy một giải pháp đơn giản với awk?

awk '{printf $0}' file

printf sẽ in mọi dòng mà không có dòng mới, nếu bạn muốn tách các dòng gốc bằng một khoảng trắng hoặc khác:

awk '{printf $0 " "}' file

echo "1\n2\n3" | awk '{printf $0}', điều này làm việc cho tôi. @ edi9999
Itachi

Bạn xin lỗi, tôi đã quên bản fin
edi9999

đây là cách tiếp cận duy nhất hiệu quả với tôi trong git bash cho windows
Plato

3

Trên Mac OS X (sử dụng sed FreeBSD):

# replace each newline with a space
printf "a\nb\nc\nd\ne\nf" | sed -E -e :a -e '$!N; s/\n/ /g; ta'
printf "a\nb\nc\nd\ne\nf" | sed -E -e :a -e '$!N; s/\n/ /g' -e ta

3

Để xóa các dòng trống:

sed -n "s/^$//;t;p;"

Đây là cho GNU Sed. Trong Sed bình thường, điều này mang lại sed: 1: "s/^$//;t;p;": undefined label ';p;'.
Léo Léopold Hertz

3

Sử dụng Awk:

awk "BEGIN { o=\"\" }  { o=o \" \" \$0 }  END { print o; }"

2
Bạn không cần phải thoát dấu ngoặc kép và ký hiệu đô la nếu bạn thay đổi dấu ngoặc kép thành dấu ngoặc đơn. Chữ "o" thường được coi là một lựa chọn xấu là một tên biến vì nó có thể bị nhầm lẫn với chữ số "0". Bạn cũng không cần phải khởi tạo biến của mình, nó mặc định là một chuỗi rỗng. Tuy nhiên, nếu bạn không muốn một không gian hàng đầu bên ngoài : awk '{s = s sp $0; sp = " "} END {print s}'. Tuy nhiên, hãy xem câu trả lời của tôi để biết cách sử dụng awk mà không cần đọc toàn bộ tệp vào bộ nhớ.
Tạm dừng cho đến khi có thông báo mới.

Vui lòng kiểm tra câu trả lời của Thor thay thế. Đó là cách hiệu quả hơn, dễ đọc hơn và tốt hơn bằng mọi cách để so sánh phương pháp này (mặc dù cách này sẽ hiệu quả)!
mschilli

Anh bạn, tôi hiểu rồi. Không cần phải chà xát vào mặt tôi :-) Dù sao câu trả lời của Thor cũng ở trên trang (đúng), vậy bạn quan tâm điều gì?
kralyk

3

Một giải pháp tôi đặc biệt thích là nối tất cả các tệp trong không gian giữ và thay thế tất cả các dòng mới ở cuối tệp:

$ (echo foo; echo bar) | sed -n 'H;${x;s/\n//g;p;}'
foobar

Tuy nhiên, có người nói với tôi rằng không gian giữ có thể là hữu hạn trong một số triển khai sed.


1
thay thế bằng một chuỗi trống trong câu trả lời của bạn che giấu thực tế rằng luôn luôn sử dụng H để nối vào không gian giữ có nghĩa là không gian giữ sẽ bắt đầu bằng một dòng mới. Để tránh điều này, bạn cần sử dụng1h;2,$H;${x;s/\n/x/g;p}
Jeff

3

Thay thế dòng mới bằng bất kỳ chuỗi nào và cũng thay thế dòng mới cuối cùng

Các trgiải pháp thuần túy chỉ có thể thay thế bằng một ký tự duy nhất và các sedgiải pháp thuần túy không thay thế dòng mới cuối cùng của đầu vào. Giải pháp sau đây khắc phục những sự cố này và dường như an toàn cho dữ liệu nhị phân (ngay cả với ngôn ngữ UTF-8):

printf '1\n2\n3\n' |
  sed 's/%/%p/g;s/@/%a/g' | tr '\n' @ | sed 's/@/<br>/g;s/%a/@/g;s/%p/%/g'

Kết quả:

1<br>2<br>3<br>

Điều này là xấu vì nó sẽ tạo ra đầu ra không mong muốn trên bất kỳ đầu vào nào có chứa@
Steven Lu

@StevenLu: Không, @trong đầu vào là OK. Nó được thoát ra %avà trở lại một lần nữa. Tuy nhiên, giải pháp có thể không hoàn toàn tuân thủ POSIX (các byte NULL không được phép vì vậy không tốt cho dữ liệu nhị phân và tất cả các dòng phải kết thúc bằng dòng mới để trđầu ra không thực sự hợp lệ).
Håkon A. Hjortland

Ah. Tôi thấy bạn đã sửa nó lên. Kinda phức tạp cho những gì nên là một hoạt động đơn giản, nhưng công việc tốt.
Steven Lu

3

Đó là sed giới thiệu các dòng mới sau khi thay thế "bình thường". Đầu tiên, nó cắt char dòng mới, sau đó nó xử lý theo hướng dẫn của bạn, sau đó nó giới thiệu một dòng mới.

Sử dụng sed, bạn có thể thay thế "phần cuối" của một dòng (không phải char dòng mới) sau khi được cắt, bằng một chuỗi bạn chọn, cho mỗi dòng đầu vào; nhưng, sed sẽ xuất ra các dòng khác nhau. Ví dụ: giả sử bạn muốn thay thế "cuối dòng" bằng "===" (tổng quát hơn thay thế bằng một khoảng trắng):

PROMPT~$ cat <<EOF |sed 's/$/===/g'
first line
second line
3rd line
EOF

first line===
second line===
3rd line===
PROMPT~$

Để thay thế char dòng mới bằng chuỗi, bạn có thể, không hiệu quả, sử dụng tr , như được chỉ ra trước đó, để thay thế ký tự dòng mới bằng "char đặc biệt" và sau đó sử dụng sed để thay thế char đặc biệt đó bằng chuỗi bạn muốn .

Ví dụ:

PROMPT~$ cat <<EOF | tr '\n' $'\x01'|sed -e 's/\x01/===/g'
first line
second line
3rd line
EOF

first line===second line===3rd line===PROMPT~$

3

Bạn cũng có thể sử dụng phương pháp này

sed 'x;G;1!h;s/\n/ /g;$!d'

Giải trình

x   - which is used to exchange the data from both space (pattern and hold).
G   - which is used to append the data from hold space to pattern space.
h   - which is used to copy the pattern space to hold space.
1!h - During first line won't copy pattern space to hold space due to \n is
      available in pattern space.
$!d - Clear the pattern space every time before getting next line until the
      last line.

Luồng:
Khi dòng đầu tiên nhận được từ đầu vào, trao đổi được thực hiện, do đó, 1 sẽ giữ không gian và \ n đến không gian mẫu, sau đó nối không gian giữ vào không gian mẫu, sau đó thay thế được thực hiện và xóa không gian mẫu.
Trong quá trình trao đổi dòng thứ hai được thực hiện, 2 chuyển sang giữ không gian và 1 đến không gian mẫu, sau đó Gnối không gian giữ vào không gian mẫu, sau đó hsao chép mẫu vào đó và thay thế được thực hiện và xóa. Thao tác này được tiếp tục cho đến khi đạt được eof sau đó in kết quả chính xác.


Tuy nhiên, được cảnh báo rằng echo 'Y' | sed 'x;G;1!h;s/\n/X/g;$!d'kết quả trong XY.
Ma quái

3

Một phương thức GNU khác sed, gần giống như câu trả lời của Zsolt Botykai , nhưng phương thức này sử dụng lệnh ( phiên âm ) sedít được sử dụng hơn , giúp tiết kiệm một byte mã (theo dõi ):yg

sed ':a;N;$!ba;y/\n/ /'

Người ta hy vọng ysẽ chạy nhanh hơn s, (có lẽ ở trtốc độ, nhanh hơn 20 lần), nhưng trong GNU sed v4.2.2 y thì chậm hơn khoảng 4%s .


Phiên bản BSD di động khác sed:

sed -e ':a' -e 'N;$!ba' -e 'y/\n/ /'

2
Với BSD sed ylà ca nhanh hơn 15%. Xem câu trả lời này cho một ví dụ làm việc.
Thor

Ngoài ra, với các lệnh sed BSD cần phải chấm dứt sau một nhãn, do đó sed -e ':a' -e 'N;$!ba' -e 'y/\n/ /'sẽ là cách để đi.
ghoti
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.