Sed thay thế cho tìm kiếm và thay thế trên các dòng rất dài


9

Tôi có các tệp được tạo bởi một chương trình không đặt dòng mới ở cuối hồ sơ. Tôi muốn đặt dòng mới giữa các bản ghi và tôi có thể làm như vậy với một tập lệnh sed đơn giản:

sed -e 's/}{/}\n{/g'

Vấn đề là các tệp đầu vào có kích thước nhiều gigabyte, và do đó, các dòng đầu vào của sed có độ dài nhiều GB. sed cố giữ một dòng trong bộ nhớ, không hoạt động trong trường hợp này. Tôi đã thử --unbufferedtùy chọn này, nhưng điều đó dường như làm cho nó chậm hơn và không cho phép nó kết thúc chính xác.


Có thể tải lên một tệp đầu vào mẫu ở đâu đó để chúng tôi thử một số ý tưởng không?
mkc

3
Có lẽ trước tiên bạn có thể sử dụng trđể dịch }sang \nvà sau đó sử dụng sedđể thêm một }ở cuối mỗi dòng? Như thế này:tr '}' '\n' < your_file.txt| sed 's/$/}/'
user43791

Việc thêm một dòng mới vào cuối tập tin có giúp ích gì không? Giống như:printf "\n" >> file
mẫu

1
@Ketan, tôi giả sử viết một tệp có 78 ký tự rác theo sau được }{lặp đi lặp lại cho đến khi nó dài vài gigabyte.
mẫu

@nanny - điểm tốt - nhưng bạn lấy 78 ở đâu? Nếu các hồ sơ đã bị chặn thì dd if=file cbs=80 conv=unblocksẽ làm điều đó - nhưng nó hiếm khi đơn giản như vậy.
mikeerv

Câu trả lời:


7

Bạn có thể sử dụng một công cụ khác cho phép bạn thiết lập dấu tách bản ghi đầu vào. Ví dụ

  • Perl

    perl -pe 'BEGIN{ $/="}{" } s/}{/}\n{/g' file
    

    Biến đặc biệt $/là dấu tách bản ghi đầu vào. Đặt nó để }{xác định các dòng là kết thúc bằng }{. Bằng cách đó bạn có thể đạt được những gì bạn muốn mà không cần đọc toàn bộ vào bộ nhớ.

  • chim ưng hoặc chim ưng

    awk -v RS="}{" -vORS= 'NR > 1 {print "}\n{"}; {print}' file 
    

    Đây là cùng một ý tưởng. RS="}{"đặt dấu tách bản ghi thành }{và sau đó bạn in }, một dòng mới, {(ngoại trừ bản ghi đầu tiên) và bản ghi hiện tại.


3

Perl để giải cứu:

perl -i~ -e ' $/ = \1024;
              while (<>) {
                  print "\n" if $closing and /^{/;
                  undef $closing;
                  s/}{/}\n{/g;
                  print;
                  $closing = 1 if /}$/;
              } ' input1 input2

Thiết $/đến \1024sẽ đọc các tập tin trong khối 1024 byte. Các $closingxử lý biến trường hợp khi một đoạn kết thúc trong }và bên cạnh một bắt đầu với {.


1
+1, có lẽ là giải pháp tốt nhất; các giải pháp perl / awk khác cũng hoạt động tốt, nhưng nếu phân tách bản ghi đầu tiên xảy ra sau khoảng 17 GB giá trị ký tự thì sao?
don_crissti

2

Bạn nên làm:

{ <infile tr \} \\n;echo {; } | paste -d'}\n' - /dev/null >outfile

Đây có lẽ là giải pháp hiệu quả nhất.

Điều đó đặt một {}để bảo vệ bất kỳ dữ liệu theo dõi có thể. Với một trquy trình nữa, bạn có thể trao đổi xung quanh và thực hiện một dòng trống ở đầu {trường đầu tiên . Giống...

tr {} '}\n'| paste -d{\\0 /dev/null - | tr {}\\n \\n{}

Vì vậy, đầu tiên, với dữ liệu ví dụ của don, không:

printf '{one}{two}{three}{four}' |
{ tr \} \\n; echo {; }           |
paste -d'}\n' - /dev/null
{one}
{two}
{three}
{four}
{}

... và cái thứ hai thì ...

printf '{one}{two}{three}{four}'      |
tr {} '}\n'| paste -d{\\0 /dev/null - |
tr {}\\n \\n{}
#leading blank
{one}
{two}
{three}
{four}

Không có dòng mới nào cho ví dụ thứ hai - mặc dù có một dòng đầu tiên.


0

Một sedtiện ích giống như nhị phân được gọi làbbe

Tôi thấy dễ dàng nhất để ở lại với cú pháp giống như sed trong trường hợp này.

Tôi rất thích sử dụng bbetiện ích (có sẵn thông qua cài đặt gói {uni, linu} x của bạn, eq apt-get). Hoặc ở đây nếu bạn là một trong số những người thích git, mặc dù tôi chưa từng xem xét liên kết cụ thể đó.

1. Nó hỗ trợ s/before/after/thành ngữ

Đó là "Trình chỉnh sửa khối nhị phân", hỗ trợ các hoạt động giống như sed (trong số các hoạt động khác). Điều này bao gồm các s/before/after/thành ngữ thay thế siêu phổ biến mà bạn cần. Lưu ý, vì không có dòng nào theo bbequan điểm, nên không có "g toàn cầu" ở cuối lệnh.

Như một bài kiểm tra nhanh (lưu ý yêu cầu -e):

$ echo hello | bbe -e 's/l/(replaced)/'

sản xuất:

he(replaced)(replaced)o

2. Trong trường hợp cụ thể của bạn }{để }\n{chuyển đổi

Vì vậy, nếu chúng ta có một tập tin đồ sộ đầy với một triệu số ở định dạng (nói) {1}{2}{3}... {1000000}không có kí tự xuống dòng, chúng ta có thể trao đổi }{với }\n{một cách dễ dàng, và có tất cả những con số một trên mỗi dòng.

Điều này sẽ là với bbelệnh này :

bbe -e 's/}{/}\n{/'

Như đã thử nghiệm trong vòng lặp zsh này, chúng tôi chỉ lấy phần đuôi của:

$ for ((num=0; num<1000000; num++)) do; echo -n "{$num}"; done | bbe -e 's/}{/}\n{/' | tail

Mà sẽ sản xuất này:

{999990}
{999991}
{999992}
{999993}
{999994}
{999995}
{999996}
{999997}
{999998}
{999999}

(tất nhiên không có sự quay trở lại của xe ngựa.)

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.