Sed альтернатива пошуку та заміни на дуже довгих рядках


9

У мене є файли, створені програмою, яка не ставила нові рядки в кінці записів. Я хочу помістити нові записи між записами, і я можу це зробити за допомогою простого сценарію sed:

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

Проблема полягає в тому, що вхідні файли мають розмір декількох гігабайт, і тому вхідні лінії до sed мають кілька ГБ. sed намагається утримувати лінію в пам'яті, що в цьому випадку не працює. Я спробував --unbufferedваріант, але це, здавалося, зробило його повільніше і не дозволило закінчити правильно.


Чи можна було б десь завантажити прикладний вхідний файл, щоб спробувати деякі ідеї?
mkc

3
Можливо, ви могли спочатку скористатись trперекладом }у, \nа потім використати, sedщоб додати а }в кінці кожного рядка? tr '}' '\n' < your_file.txt| sed 's/$/}/'
Ось так

Чи допомагає додавання нового рядка в кінці файлу взагалі? Як:printf "\n" >> file
няня

1
@ Ketana, я припускаю, що запис файлу з 78 символами сміття слід }{повторювати, поки не вистачить кількох гігабайт.
няня

@nanny - хороший момент - але де ти береш 78? Якщо записи вже заблоковані, dd if=file cbs=80 conv=unblockце робиться, але це рідко так просто.
mikeserv

Відповіді:


7

Можна скористатися іншим інструментом, який дозволяє встановити роздільник запису вхідних даних. Наприклад

  • Perl

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

    Спеціальна змінна $/- роздільник запису вхідних даних. Встановивши його, }{визначає рядки як закінчення }{. Таким чином ви зможете досягти того, що хочете, не прочитавши всі речі в пам'яті.

  • мавк або гаук

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

    Це та сама ідея. RS="}{"встановлює роздільник записів на, }{а потім ви друкуєте }, новий рядок {(крім першого запису) та поточний запис.


3

Перл на допомогу:

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

Установка $/для \1024прочитає файл в шматках 1024 байт. В $closingзмінної обробляє випадок , коли шматок закінчується в , }а наступний починається з {.


1
+1, мабуть, найкраще рішення; інші рішення perl / awk теж добре працюють, але що робити, якщо перший роздільник записів виникає після знаків, що мають значення близько 17 ГБ?
don_crissti

2

Вам слід зробити:

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

Це, мабуть, найбільш ефективне рішення.

Це ставить a {}для захисту будь-яких можливих даних, що відкладаються . Ще одним trпроцесом ви можете поміняти місцями навколо цього місця і зробити порожній рядок на чолі першого {поля. Подібно до...

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

Отже, перший, з прикладом даних Дона, робить:

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

... а другий робить ...

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

Немає останньої нової лінії для другого прикладу - хоча є перша для першого.


0

Бінарна sedутиліта називаєтьсяbbe

Мені найлегше в цьому випадку залишитися з синтаксисом, що нагадує sed.

Я дуже вважаю за краще використовувати bbeутиліту (доступну через встановлення пакунків {uni, linu} x, eq apt-get). Або тут, якщо ви один з натовпу git, хоча я особисто не перевіряв саме цю посилання.

1. Він підтримує s/before/after/ідіому

Це "Бінарний редактор блоків", який підтримує сім-подібні (серед інших) операції. Сюди входить супер поширена s/before/after/ідіома заміщення, яка вам потрібна. Зауважте, оскільки рядків самі по собі з bbeточки зору немає, в кінці команди немає "глобального g".

Як швидкий тест (зверніть увагу на необхідне -e):

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

виробляє:

he(replaced)(replaced)o

2. У вашому конкретному випадку }{для }\n{перетворення

Так що, якщо у нас був масивний файл , заповнений мільйон чисел в (скажімо) в форматі {1}{2}{3}... {1000000}без будь - яких повернення каретки, ми могли б обміняти }{з }\n{легкістю, і все числа по одному в кожному рядку.

Це було б із цією bbeкомандою:

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

Як перевірено в цьому циклі zsh, який ми хапаємо лише за хвіст:

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

Що б призвело до цього:

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

(без зворотного повернення каретки, звичайно.)

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.