Sed alternatywa dla wyszukiwania i zamiany na bardzo długie linie

9

Mam pliki, które zostały wygenerowane przez program, który nie umieścił znaku nowej linii na końcu rekordów. Chcę wstawić znaki nowego wiersza między rekordami i mogę to zrobić za pomocą prostego skryptu sed:

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

Problem polega na tym, że pliki wejściowe mają rozmiar wielu gigabajtów, a zatem linie wejściowe do sed mają wiele GB. sed próbuje utrzymać linię w pamięci, co w tym przypadku nie działa. Wypróbowałem tę --unbufferedopcję, ale wydawało się, że spowalnia ją i nie pozwala poprawnie zakończyć.

Tom Panning
źródło
Czy byłoby możliwe przesłanie gdzieś przykładowego pliku wejściowego, abyśmy mogli wypróbować jakieś pomysły?
mkc
3
Być może można najpierw użyć trdo przetłumaczenia }na \n, a następnie użyć sed, aby dodać }na końcu każdego wiersza? W ten sposób:tr '}' '\n' < your_file.txt| sed 's/$/}/'
user43791,
Czy dodanie nowej linii na końcu pliku w ogóle pomaga? Jak:printf "\n" >> file
niania
1
@Ketan, zakładam, że pisanie pliku z 78 śmieciowymi znakami będzie }{powtarzane, aż wystarczy kilka gigabajtów.
niania
@nanny - dobry punkt - ale skąd masz 78? Jeśli rekordy są już zablokowane dd if=file cbs=80 conv=unblock, zrobiłby to - ale rzadko jest to takie proste.
mikeserv

Odpowiedzi:

7

Możesz użyć innego narzędzia, które pozwala ustawić separator rekordów wejściowych. Na przykład

  • Perl

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

    Zmienna specjalna $/to separator rekordów wejściowych. Ustawienie go }{definiuje linie jako kończące się na }{. W ten sposób możesz osiągnąć to, co chcesz, bez wczytywania całej pamięci do pamięci.

  • jastrząb lub gawk

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

    To ten sam pomysł. RS="}{"ustawia separator rekordów na, }{a następnie drukuje }, nowy wiersz {(z wyjątkiem pierwszego rekordu) i bieżący rekord.

terdon
źródło
3

Perl na ratunek:

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

Ustawienie, $/aby \1024odczytać plik w kawałkach 1024 bajtów. Te $closinguchwyty zmienne przypadku, gdy skończy się chunk w }a kolejny rozpoczyna się {.

choroba
źródło
1
+1, prawdopodobnie najlepsze rozwiązanie; inne rozwiązania perl / awk też działają dobrze, ale co jeśli pierwszy separator rekordów pojawi się po około 17 GB znaków?
don_crissti
2

Powinieneś zrobić:

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

To chyba najbardziej wydajne rozwiązanie.

Która stawia {}chronić ewentualne dane spływu. Za pomocą jeszcze jednego trprocesu możesz to zamienić i zrobić pustą linię na początku pierwszego {pola. Lubić...

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

Tak więc pierwsza z przykładowymi danymi don:

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

... a drugi robi ...

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

W drugim przykładzie nie ma końca nowego wiersza - chociaż jest jeden dla pierwszego.

mikeserv
źródło
0

sedNarzędzie binarne o nazwiebbe

W tym przypadku uważam, że najłatwiej jest pozostać przy składni podobnej do sed.

I wiele wolą za pomocą bbenarzędzia (dostępne za pośrednictwem {Uni, Linu} instalacji pakiet X., eq apt-get). Lub tutaj, jeśli jesteś jednym z gitów, chociaż ja osobiście nie sprawdziłem tego konkretnego linku.

1. Obsługuje s/before/after/idiom

Jest to „Binary Block Editor”, który obsługuje operacje podobne do sed (między innymi). Obejmuje to bardzo popularny s/before/after/idiom podstawienia, którego potrzebujesz. Uwaga: ponieważ z bbepunktu widzenia nie ma linii jako takich, na końcu polecenia nie ma „globalnego g”.

Jako szybki test (zwróć uwagę na wymagane -e):

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

produkuje:

he(replaced)(replaced)o

2. W twoim przypadku konkretnego dnia }{do }\n{konwersji

Więc jeśli mieliśmy ogromny plik wypełniony milionów numerów (powiedzmy) w formacie {1}{2}{3}... {1000000}bez powrotu karetki, możemy wymieniać }{się }\n{łatwo i mają wszystkie numery jeden na linię.

Byłoby to z tym bbepoleceniem:

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

Jak przetestowaliśmy w tej pętli zsh, którą chwytamy za ogon:

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

Który spowodowałby to:

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

(oczywiście bez powrotu karetki tylnej).

tgm1024 - Monica była źle traktowana
źródło