Usuń znak nowej linii tylko co N linii


16

Przetwarzając tekst, muszę usuwać znak nowej linii co dwa wiersze.

Przykładowy tekst:

this is line one
and this is line two
the third and the
fourth must be pasted too

Pożądane wyjście:

this is line one and this is line two
the third and the fourth must be pasted too

Próbowałem whilepętli, ale pętla while jest złą praktyką. Czy można to zrobić za pomocą trlub innego polecenia?


4
Tytuł mówi „co N linii”, ale w pytaniu i przykładzie jest to „co 2 linie”. Większość odpowiedzi działa tylko dla N = 2. Szukasz czegoś, co działa dla wszystkich N?
JigglyNaga,

To jest klucz. Wszyscy odpowiedzieli na 2 wiersze, ale musiałbym użyć N = 3 lub N = 4
jomaweb

Odpowiedzi:


24

paste(również standardowe proste narzędzie POSIX, takie jak tr) jest do tego narzędziem.

Zakładając, że chcesz, aby znaki nowego wiersza zostały zastąpione spacją, a nie tylko usunięte, jak w przykładzie:

paste -d ' ' - - < file

Lub:

paste -sd ' \n' file

Wymienić ' 'ze '\0'jeśli rzeczywiście chcą zrobić je usunąć.

Aby zamienić 2 z 3:

paste -sd '  \n' file

1 z 3, zaczynając od drugiego:

paste -sd '\n \n' file

I tak dalej.

Kolejną dobrą rzeczą pastejest to, że nie pozostawia linii nie zakończonej. Na przykład, jeśli usuniesz każdą nową linię w pliku (jak z tr -d '\n' < filelub tr '\n' ' ' < file), w końcu nie będzie żadnej linii, ponieważ linie muszą być zakończone znakiem nowej linii. Dlatego ogólnie lepiej jest używać pastezamiast tego (jak w paste -sd '\0' filelub paste -sd ' ' file), który doda końcowy znak nowej linii niezbędny do uzyskania prawidłowego tekstu.


11

Z nowoczesnym GNU sed

sed -rz 's/\n([^\n]*\n)/ \1/g' sample.text

I awk

awk '{getline line2;print $0, line2}' sample.text

3
Takie sedpodejście oznacza zamazanie całego pliku w pamięci (pod warunkiem, że nie zawiera on bajtów NUL) i wykonanie kosztownego podstawienia wyrażenia regularnego. Nie widzę przewagi nad standardowym sed 'N;s/\n/ /'podejściem.
Stéphane Chazelas,

6

Użyj seddo tego, jak pokazano poniżej:

SHW@SHW:/tmp $ cat a
this is line one
and this is line two
the third and the
fourth must be pasted too

SHW@SHW:/tmp $ sed 'N;s/\n/ /' a -i

SHW@SHW:/tmp $ cat a
this is line one and this is line two
the third and the fourth must be pasted too

4

Innym sposobem jest użycie xargs:

$ < txt xargs -d '\n' -n 2 echo
this is line one and this is line two
the third and the fourth must be pasted too

gdzie

$ cat txt
this is line one
and this is line two
the third and the
fourth must be pasted too

Chociaż to rozwiązanie jest dość nadmierne, ponieważ echoproces jest wykonywany dla każdej linii ... Dlatego oprócz przykładów zabawek powinno być preferowane rozwiązanie oparte na awk / sed lub podobne.


1
W zależności od echoimplementacji będziesz mieć również problemy ze znakami odwrotnego ukośnika lub niektórymi wierszami rozpoczynającymi się od -(jak --helplub -neneGNU echo). Zauważ też, że -djest to rozszerzenie GNU.
Stéphane Chazelas,

Aby uniknąć problemów echo, możesz użyć tego:< txt xargs -d '\n' -n 2 printf -- '%s %s\n'
nyuszika7h

4

Jest to bardzo proste w vimie. Aby dołączyć do każdej linii, użyj Jpolecenia, a następnie użyj %normpolecenia, aby zastosować go do każdej linii jednocześnie. Na przykład

:%norm J<CR>

(W przypadku, gdy nie jesteś obeznany z vimem, <CR>oznacza to tylko enter)

Działa to nawet w celu połączenia dowolnej liczby linii. Na przykład dołączenie co dziesięć linii byłoby

:%norm 9J<CR>

Jeśli czujesz się niekomfortowo z vimem i wolisz używać go jako narzędzia wiersza poleceń zamiast interaktywnego edytora tekstu, możesz:

vim myfile -c '%norm J' -c 'wq'

Czy downvoter chciałby wyjaśnić, co mogę zrobić, aby poprawić tę odpowiedź?
DJMcMayhem

3
$ awk '{printf "%s%s",$0,(NR%2?" ":"\n")}' sample.txt
this is line one and this is line two
the third and the fourth must be pasted too

Spowoduje to wydrukowanie każdej linii, $0a następnie spacji lub nowej linii w zależności od tego, czy numer linii NRjest nieparzysty, czy parzysty.

Wyrażenie NR%2?" ":"\n"jest wyrażeniem potrójnym. Wyrażenie ma NR%2wartość true (niezerową), jeśli numer wiersza jest nieparzysty. W tym przypadku wyrażenie trójskładnikowe zwraca spację. Jeśli wartość ma wartość false (zero), zwracana jest nowa linia.

Alternatywny

Jak sugeruje Costas w komentarzach:

$ awk '{ORS=(NR%2?" ":RS)}1' sample.txt
this is line one and this is line two
the third and the fourth must be pasted too

W tym przypadku instrukcja trójskładnikowa NR%2?" ":RSsłuży do zwracania spacji lub separatora rekordów wejściowych ( RSdomyślnie = nowy wiersz). Wartość ta jest przypisana do rekordu separatora wyjściowego ORS. Na 1końcu polecenia znajduje się tajemniczy skrót awk do wydrukowania rekordu.


Nadal możesz zapisać 3 znaki: ()nawiasy i spację po printf;)
maxschlepzig

1
Potrójny? O! 'NR%2{printf("%s ",$0);next}1'
Costas

Z odpowiedzią maxschlepzig i oświadczeniem potrójnym:'{ORS=(NR%2?" ":RS)}1'
Costas

@Costas Podoba mi się to. Odpowiedź zaktualizowana o ORSrozwiązanie.
John1024,

2

Ogólne rozwiązanie, zastąp 5wymaganą liczbą linii

$ # eof to ensure last line has newline ending
$ seq 16 | perl -pe 's/\n/ / if ++$i%5 && !eof'
1 2 3 4 5
6 7 8 9 10
11 12 13 14 15
16

$ # or just use pr
$ seq 16 | pr -5ats' '
1 2 3 4 5
6 7 8 9 10
11 12 13 14 15
16

1

Możesz użyć awkdo tego:

$ awk '{c="\n"} NR%2 {c=" "} { printf("%s%s", $0, c) } ' txt

Produkuje:

this is line one and this is line two
the third and the fourth must be pasted too

gdzie:

$ cat txt
this is line one
and this is line two
the third and the
fourth must be pasted too

Te awkdziałania są wykonywane dla każdej linii, specjalne zmienne $0odniesienia bieżącej linii, NRjest obecny numer linii (zaczynając od 1). Druga akcja jest chroniona przez wyrażenie NR%2, które jest operacją modulo. Dlatego c=" "jest wykonywany tylko wtedy, gdy NR%2jest prawdziwy, tj. Dla nieparzystych numerów linii.

awkSkładnia jest jak C, ale niektóre elementy są opcjonalne w niektórych sytuacjach - np średniki.


Twoja czmienna to ORS:'NR%2{ORS=" "}1;{ORS=RS}'
Costas

0

Używanie ed:

$ cat text
this is line one
and this is line two
the third and the
fourth must be pasted too
this is line one
and this is line two
the third and the
fourth must be pasted too

$ ed text <<'END_ED'
g/./s/$/ /\
j
w text.new
END_ED
164
164

$ cat text.new
this is line one and this is line two
the third and the fourth must be pasted too
this is line one and this is line two
the third and the fourth must be pasted too

Polecenia ededycyjne dla każdego wiersza ( gzastosują zestaw poleceń edycyjnych do każdego wiersza pasującego do podanego wyrażenia regularnego) dodają znak spacji na końcu i łączą go z następnym wierszem. Następnie zapisuje wynikowy tekst do pliku o nazwie text.new.


0

Z Ruby.

Zakładam, że każdy blok nlinii ma zostać połączony. Załóżmy n = 3, że plik wejściowy jest, 'infile'a wyniki mają zostać zapisane do pliku 'outfile'.

Skonstruuj plik

Ruby -e "File.write 'infile', <<_
> Line 1
> Line 2
> Line 3
> Line 4
> Line 5
> Line 6
> Line 7
> _"

Potwierdź zawartość pliku

ruby -e "p File.read 'infile'"
  # "Line 1\nLine 2\nLine 3\nLine 4\nLine 5\nLine 6\nLine 7\n"

Usuń nowe linie i zapisz do pliku

ruby -e "File.write 'outfile', File.readlines('infile').
  each_with_index { |line,i| line.chomp! unless (i+1)%3==0 }"

Potwierdź zawartość

ruby -e "puts File.read 'outfile'"
  # ["Line 1", "Line 2", "Line 3\n", "Line 4", "Line 5", "Line 6\n", "Line 7"]

1
Dobry. Teoretycznie rubyjest nie na temat U&L. Ale ponieważ używasz go z wiersza polecenia ruby -e, dzięki czemu jest wystarczająco tematyczny.
grochmal
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.