Câu hỏi Làm thế nào tôi có thể thay thế một dòng mới (\ n) bằng cách sử dụng sed?


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

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 chữa nó?


1128
2017-08-09 19:10


gốc


tr chỉ là công cụ thích hợp cho công việc nếu thay thế một ký tự cho một ký tự đơn, trong khi ví dụ trên cho thấy thay thế dòng mới bằng dấu cách .. Vì vậy, trong ví dụ trên, tr có thể hoạt động .. Nhưng sẽ bị giới hạn sau này. - Mayhem
@Mayhem, sed 's/$/ NewDelim/' | tr '\n' ' '. Sử dụng sed để thêm dấu phân tách mới vào cuối mỗi dòng, sau đó xóa các dòng mới bằng tr. Ít khó hiểu hơn sed cách duy nhất, IMO. - cp.engr
tr trong công cụ thích hợp cho công việc bởi 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à duy nhất phức tạp cho sed nhưng dễ dàng thực hiện bởi tr. Đây là một câu hỏi phổ biến. Thực hiện thay thế regex không được thực hiện bởi tr nhưng bởi sed, đó sẽ là công cụ thích hợp ... cho một câu hỏi khác. - Mike S
"tr" cũng có thể chỉ 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ổ biến hơn `tr -d '\ 012 \ 015'`. - anthony
CẢNH BÁO: "tr" hoạt động khác nhau liên quan đến một phạm vi ký tự giữa Linux và các máy Solaris cũ hơn (EG sol5.8). EG: `tr -d 'a-z'` và `tr -d '[a-z]'`. Cho rằng tôi khuyên bạn nên sử dụng "sed" mà không có sự khác biệt đó. - anthony


Các câu trả lời:


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 tin trong một vòng lặp, sau đó thay thế (các) dòng mới bằng dấu cách.

Giải trình:

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

Đâ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 @Benjie bình luận):

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

1281
2017-08-09 20:26



@Arjan và Masi: OS X sử dụng BSD sed chứ không phải 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 coreutils và findutils trên OS X và bỏ qua các phiên bản BSD. - Telemachus
Các :a không phải là một đăng ký, đó là một nhãn chi nhánh. Đó là mục tiêu cho b lệnh * hoạt động như "goto". Gọi nó là một đăng ký ngụ ý rằng bạn có thể tạo ra các địa điểm lưu trữ. Chỉ có hai "thanh ghi"; một được gọi là "không gian lưu giữ" mà tập lệnh của bạn không sử dụng và tập lệnh kia được gọi là "không gian mẫu". Các N lệnh nối thêm một dòng mới và dòng tiếp theo của tệp đầu vào vào vùng mẫu. [* Bạn có thể có nhiều nhãn & b lệnh. Nếu bạn có một b lệnh mà không có nhãn char được gắn thêm vào nó, nó sẽ rẽ nhánh vào cuối tập lệnh để đọc dòng tiếp theo và lặp lại.] - Dennis Williamson
Bạn có thể chạy nền tảng chéo 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ì tách bằng dấu chấm phẩy: sed -e ':a' -e 'N' -e '$!ba' -e 's/\n/ /g' - Benjie
Tại sao không phải bất cứ ai bình luận về những gì một đống lộn xộn này là (không phải là câu trả lời chính nó, 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 mà thường chạy tốt, nhưng nếu bạn muốn lái xe đến một con phố gần đó cụ thể, cách duy nhất là để nâng chiếc xe với một máy bay trực thăng. - Ark-kun
Come on folks - 261 upvotes cho một giải pháp điên rồ, không thể hiểu nổi mà không hoạt động ???? sed là một công cụ tuyệt vời cho các subsitutions đơ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. Tốt đau buồn .... - Ed Morton


Sử dụng tr thay thế?

tr '\n' ' ' < input_filename

hoặc xóa hoàn toàn 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 của nó)

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

1448
2017-08-09 19:16



Sed là dựa trên dòng do đó rất khó để nắm bắt dòng mới. - Alexander Gladysh
sed hoạt động trên một "dòng" của đầu vào, nhưng nó hiểu nó trong các đoạn được phân tách bằng dòng mới. Nó là một công cụ unix, có nghĩa là nó làm một điều rất tốt. Một điều là "làm việc trên một dòng file-khôn ngoan". Làm cho nó làm điều gì đó khác sẽ khó khăn, và rủi ro là 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ể làm cho công cụ này làm điều gì đó mà nó không bao giờ có nghĩa là để làm gì?" 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 đang làm sai. - dmckee
1 Để sử dụng đúng công cụ cho công việc. - poindexter
tr là rất tốt, nhưng bạn chỉ có thể thay thế dòng mới bằng các ký tự đơn. Bạn cần phải sử dụng một công cụ khác nếu bạn muốn thay thế dòng mới bằng một chuỗi - Eddy
@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 mà tôi muốn sử dụng - rjohnston


Trả lời nhanh:

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

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


Giải pháp thay thế:

Tất cả các lựa chọn thay thế, không giống như quyến rũ sẽ không cần phải tiếp cậ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, quyến rũ-tốc độ giống nhau

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

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

tr '\n' ' ' < file

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

paste -s -d ' ' file

với lúng túng, tr-tốc độ giống nhau

awk 1 ORS=' ' file

Thay thế khác như "echo $ (<file)" 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 \ n thoát
      trình tự? Tại sao tôi không thể khớp 2 hoặc nhiều dòng 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ị tước trước khi dòng được đặt vào
   không gian mẫu. Để 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 một dòng tại một thời điểm, cắt bỏ
   chấm dứt dòng mới, đặt những gì còn lại vào không gian mẫu
   tập lệnh sed có thể giải quyết hoặc thay đổi nó và khi không gian mẫu
   được in, thêm một dòng mới vào stdout (hoặc một tệp). Nếu
   không gian mẫu được xóa hoàn toàn hoặc một phần bằng 'd' hoặc 'D',
dòng mới là không phải được thêm vào trong các trường hợp như vậy. Do đó, các tập lệnh 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Ờ làm việc, bởi vì dòng mới được xóa trước
   dòng được đưa vào không gian mẫu. Để thực hiện các tác vụ trên,
   sử dụng một trong các tập lệnh sau để thay thế:

  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          

Vì 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, 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 dòng mới, GNU sed sẽ thêm
   rằng dòng mới đến đầu ra nhưng xóa tất cả những người 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 thêm dòng hiện tại
   vào không gian Giữ, và sau đó lấy các dòng từ không gian lưu giữ
   với x, g hoặc G; hoặc (3) sử dụng các dải địa chỉ (xem phần 3.3 ở trên)
   để khớp các dòng giữa hai địa chỉ được chỉ định.

Lựa chọn (1) và (2) sẽ đặt \ n vào không gian mẫu, tại đó
   có thể được giải quyết như mong muốn ('s / ABC \ nXYZ / bảng chữ cái / g'). Một ví dụ
   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 thế nào để xóa một khối riêng các dòng liên tiếp? ").
   ví dụ có thể được sửa đổi bằng cách thay đổi lệnh xóa thành một cái gì đó
   khác, như 'p' (in), 'i' (chèn), 'c' (thay đổi), 'a' (phụ lục),
   hoặc 's' (thay thế).

Lựa chọn (3) sẽ không đặt \ n vào không gian mẫu, nhưng làm
   phù hợp với một khối các dòng liên tiếp, vì vậy có thể bạn không
   thậm chí cần \ n để tìm những gì bạn đang tìm kiếm. Vì GNU sed
   Phiên bản 3.02.80 giờ đây hỗ trợ cú pháp này:

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

ngoài phạm vi truyền thống '/ from here /, / to there / {...}'
   địa chỉ, có thể tránh được việc sử dụng hoàn toàn.


414
2017-10-08 14:55



tr là một ý tưởng tuyệt vời và phạm vi phủ sóng tổng thể của bạn tạo ra câu trả lời có chất lượng hàng đầu. - New Alexandria
+1 để sử dụng (tiện ích tiêu chuẩn) paste... và tất cả những thứ khác! - Totor
@elgalu thử cái này unix.stackexchange.com/questions/4527/… - hdorio
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à tại sao lệnh hoạt động. - pdwalker
Điều này có thể hữu ích nhất trong 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ý nhiều đối sánh nhân vậ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


Một lựa chọn thay thế ngắn hơn:

awk 1 ORS=' '

Giải trình

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

condition { code-block }

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

Khi nào awk đọc đầu vào nó chia nó thành các bản ghi dựa trên giá trị của RS (Record Separator), theo mặc định là một dòng mới, do đó awk theo mặc định sẽ phân tích cú pháp dòng đầu vào. Việc tách cũng liên quan đến việc tước bỏ RS từ bả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 nối thêm vào nó (mặc định là một dòng mới). Vì vậy, bằng cách thay đổi ORSđến một không gian, tất cả các dòng mới được thay đổi thành không gian.


176
2018-02-13 12:12



rõ ràng, đơn giản, thanh lịch và hoạt động! +1. - Ed Morton
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
Nếu nó có ý nghĩa hơn, điều này có hiệu quả có thể được viết như sau: awk 'BEGIN { ORS=" " } { print $0 } END { print "\n"} ' file.txt (thêm một dòng kết thúc mới chỉ để minh họa bắt đầu / kết thúc); "1" đánh giá 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ỉ làm việc trên các dòng khớp với mẫu: awk 'BEGIN { ORS=" " } /pattern/ { print $0 } END { print "\n"} ' - michael
Tôi thích cách tiếp cận này, cần có thêm nhiều upvotes IMHO. - Panagiotis Moustafellos
Bạn có thể làm điều đó nhiều hơn simle: code awk 'ORS = ""' file.txt code - Udi


gnu sed có một lựa chọn -z cho các bản ghi được tách riêng (dòng). Bạn chỉ có thể gọi:

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

93
2018-05-05 09:43



Ngay cả khi đầu vào có chứa null, chúng sẽ được giữ nguyên (như các dấu phân cách bản ghi). - Toby Speight
@TobySpeight, cảm ơn bạn. Bạn đúng rồi! - JJoao
Sẽ không tải toàn bộ đầu vào nếu không có null không? Trong trường hợp này, việc xử lý tệp nhiều gigabyte có thể bị lỗi. - Ruslan
@ Ruslan, có 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
Đây là nghiêm trọng tốt câu trả lời. Các biểu thức khác quá méo mó để nhớ. @JJoao Bạn có thể sử dụng nó với -u, --unbuffered. Các man trạng thái mage: "tải lượng dữ liệu tối thiểu từ các tệp đầu vào và xóa bộ đệm đầu ra thường xuyên hơn". - not2qubit


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 ý kiến, cần lưu ý rằng các chỉnh sửa này đã được áp dụng. -i.bak sẽ 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 hiện thông thường không thông minh như bạn nghĩ.


77
2017-08-09 19:25



Xin vui lòng ít nhất đề cập đến -i mà không có hậu tố không sao lưu. -i.bak bảo vệ bạn khỏi một sai lầm dễ dàng, xấu xí (nói, quên loại -p và zeroing ra các tập tin). - Telemachus
@ Telemachus: Đó là một điểm công bằng, nhưng nó có thể được lập luận theo một trong hai cách. Lý do chính mà tôi không đề cập đến là ví dụ sed trong câu hỏi của OP không sao lưu, vì vậy nó có vẻ không cần thiết ở đây. Lý do khác là tôi đã không bao giờ thực sự sử dụng chức năng sao lưu (tôi thấy sao lưu tự động gây phiền nhiễu, thực sự), vì vậy tôi luôn 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ự. Đối với tốt hơn hoặc tệ hơn (có lẽ tồi tệ hơn), tôi là một tối giản cưỡng chế; Tôi chỉ thích 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
@ Ire_and_curses: Thực ra, bạn vừa tạo ra một lý lẽ tốt để bỏ qua tôi. Đó là, bạn có lý do cho sự lựa chọn của bạn, và có hay không tôi đồng ý với các lựa chọn, tôi chắc chắn tôn trọng điều đó. Tôi không chắc chắn lý do tại sao, nhưng tôi đã có một giọt nước mắt về điều này đặc biệt gần đây (các -i 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
@ire_and_curses: Tôi vừa kiểm tra, và tôi đã không nhận ra rằng tôi đã làm phiền bạn đặc biệt về điều này hai lần trong hai hoặc ba ngày qua. Đã đến lúc tôi buông bỏ vấn đề cụ thể này và đi dạo, tôi nghĩ vậy. - Telemachus
@ Telemachus: Không sao cả. Tôi đã chỉnh sửa câu trả lời cho hậu thế. Hy vọng bạn thích đi bộ. ;) - ire_and_curses


Ai cần sed? Đây là bash đường:

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

40
2017-08-11 12:07



Upvote, tôi thường sử dụng câu trả lời hàng đầu, nhưng khi đường ống / dev / urandom thông qua nó, sed sẽ không in cho đến EOF, và ^ C không có EOF. Giải pháp này in mỗi khi 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
thì tại sao không: echo -n `cat days.txt` Từ bài đăng này - Tony
@Tony vì backticks không được chấp nhận và con mèo là thừa ;-) Sử dụng: echo $ (<days.txt) - seumasmac
Thậm chí không sử dụng cat: while read line; do echo -n "$line "; done < test.txt. Có thể hữu ích nếu một sub-shell là một vấn đề. - Carlo Cannas
echo $(<file) bóp tất cả các khoảng trống cho một không gian duy nhất, không chỉ các dòng mới: điều này vượt xa những gì mà OP đang hỏi. - glenn jackman


Để thay thế tất cả các dòng mới bằng không gian sử dụ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 có 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 ký tự không phải là dấu cách:

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

24
2018-03-30 06:41



Điều này làm việc cho tôi, nơi sed giải pháp không - New Alexandria


Ba thứ.

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

  2. stream! = dòng dựa. ed là trình chỉnh sửa dựa trên dòng. sed không phải là. Xem sed bài giảng để biết thêm thông tin về sự khác biệt. Hầu hết mọi người lẫn lộn sed mặc định, không phải là rất tham lam trong mẫu phù hợp với các đối sánh 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ự, mặc định nó chỉ thay thế trên khớp đầu tiên mà nó tìm thấy ( trừ khi được quy định khác bằng lệnh chung). Thậm chí sẽ không có một lệnh chung nếu nó dựa trên dòng chứ không phải 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. Thử chạy ed; bạn sẽ nhận thấy sự khác biệt. ed là khá hữu ích nếu bạn muốn lặp qua các dòng cụ thể (chẳng hạn như trong một vòng lặp), 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
    

    hoạt động tốt trong GNU sed phiên bản 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. Đó là xấu xí và hơi cồng kềnh để gõ vào, nhưng nó hoạt động tốt. Các {}có thể bị bỏ đi, vì chúng chỉ được đưa vào vì lý do an toàn.


20
2018-05-01 11:04



Những gì là one? Tôi không biết lệnh này! - brandizzi
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 về những gì bạn có thể làm với sed nhưng thay vào đó là cách dễ dàng để hiểu những gì đang xảy ra. Tôi có một thời gian rất khó làm việc với sed vì 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 q như nhảy có điều kiện, nó hoạt động với một kiểu như s/\n / / (để tham gia tất cả các dòng bắt đầu bằng một 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 các tệp đa megabyte. - textshell
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 là gần 800 lần chậm hơ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