Jak mogę zastąpić znak nowej linii (\ n) za pomocą sed?


1370

Jak mogę zastąpić znak nowej linii („ \n”) spacją („ ”) za pomocą sedpolecenia?

Bezskutecznie próbowałem:

sed 's#\n# #g' file
sed 's#^$# #g' file

Jak to naprawić?


27
trjest tylko odpowiednim narzędziem do zadania, jeśli zamienisz pojedynczy znak na pojedynczy znak, podczas gdy powyższy przykład pokazuje zamień znak nowej linii spacją .. Więc w powyższym przykładzie tr może działać .. Ale będzie później ograniczać.
Angry 84

9
trw odpowiednim narzędziu do pracy, ponieważ pytający chciał zastąpić każdą nową linię spacją, jak pokazano w jego przykładzie. Zastąpienie nowych linii jest wyjątkowo tajemnicze, sedale łatwe do wykonania tr. To jest częste pytanie. Wykonanie zamiany wyrażenia regularnego nie jest wykonywane przez, trale przez sed, co byłoby właściwym narzędziem ... na inne pytanie.
Mike S

3
„tr” może również po prostu usunąć nowy wiersz `tr -d '\ n' ', ale możesz również chcieć usunąć znaki powrotu, aby być bardziej uniwersalnym` tr -d' \ 012 \ 015 ''.
Anthony

2
OSTRZEŻENIE: „tr” działa inaczej w zakresie zakresów znaków między Linuksem a starszymi komputerami Solaris (EG sol5.8). EG: `tr -d 'az' 'i` tr -d' [az] ''. Do tego polecam użyć „sed”, który nie ma tej różnicy.
Anthony

2
@MikeS Dzięki za odpowiedź. Postępuj zgodnie tr '\012' ' 'z echo. W przeciwnym razie usuwany jest również ostatni kanał w pliku. tr '\012' ' ' < filename; echoZrób sztuczkę.
Bernie Reiter,

Odpowiedzi:


1513

Użyj tego rozwiązania z GNU sed:

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

Spowoduje to odczytanie całego pliku w pętli, a następnie zastąpienie nowego wiersza (ów) spacją.

Wyjaśnienie:

  1. Utwórz etykietę za pomocą :a.
  2. Dołącz bieżącą i następną linię do przestrzeni wzorów za pomocą N.
  3. Jeśli znajdujemy się przed ostatnim wierszem, przejdź do utworzonej etykiety $!ba( $!oznacza to, że nie należy tego robić w ostatnim wierszu, ponieważ powinna istnieć jedna końcowa nowa linia).
  4. W końcu podstawienie zastępuje każdą nową linię spacją w przestrzeni wzorców (która jest całym plikiem).

Oto składnia kompatybilna z wieloma platformami, która działa z BSD i OS X sed(zgodnie z komentarzem @Benjie ):

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

Jak widać, użycie sedtego prostego problemu jest problematyczne. Aby uzyskać prostsze i odpowiednie rozwiązanie, zobacz tę odpowiedź .


45
@Arjan i Masi: OS X używa BSD sedzamiast GNU sed, więc mogą występować pewne subtelne (i niektóre nie tak subtelne) różnice między nimi. Jest to ciągły ból, jeśli pracujesz zarówno na komputerach z systemem OS X, jak i * nix. Zwykle instaluję GNU coreutilsi findutilsOS OS i ignoruję wersje BSD.
Telemachus

50
To :anie jest rejestr, to etykieta oddziału. Jest to cel bpolecenia *, który działa jak „goto”. Nazwanie go rejestrem oznacza, że ​​możesz tworzyć lokalizacje pamięci. Istnieją tylko dwa „rejestry”; jedna nazywa się „przestrzenią wstrzymania”, której skrypt nie używa, a druga „przestrzenią wzorców”. NPolecenie dołącza do nowej linii i i następny wiersz pliku wejściowego do przestrzeni wzorca. [* Możesz mieć wiele etykiet i bpoleceń. Jeśli masz bpolecenie bez dołączonego do niego znaku etykiety, rozgałęzia się ono na końcu skryptu, aby przeczytać następny wiersz i zapętlić ponownie.]
Wstrzymano do odwołania.

108
Możesz uruchomić tę platformę (np. W systemie Mac OS X), wykonując osobno polecenia zamiast oddzielając je średnikami: sed -e ':a' -e 'N' -e '$!ba' -e 's/\n/ /g'
Benjie

74
Dlaczego nikt nie komentuje, jaki to głupi bałagan (nie sama odpowiedź, ale program, dla którego proponowana odpowiedź jest najlepszym rozwiązaniem bardzo prostego problemu). Sed wygląda jak samochód, który zwykle działa dobrze, ale jeśli chcesz jechać na określoną pobliską ulicę, jedynym sposobem jest podniesienie samochodu za pomocą helikoptera.
Ark-kun

12
Chodźcie ludzie - 261 głosów za szalonym, niezrozumiałym rozwiązaniem, które nie działa ???? sed jest doskonałym narzędziem do prostych zapisów w jednej linii, do wszystkiego innego wystarczy użyć awk. Dobry żal ....
Ed Morton

1711

sedjest przeznaczony do użycia z danymi wejściowymi opartymi na linii. Chociaż może zrobić to, czego potrzebujesz.


Lepszą opcją jest tutaj użycie trpolecenia w następujący sposób:

tr '\n' ' ' < input_filename

lub całkowicie usuń znaki nowego wiersza:

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

lub jeśli masz wersję GNU (z jej długimi opcjami)

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

88
Sed jest oparty na linii, dlatego trudno jest uchwycić nowe linie.
Alexander Gladysh

191
sed działa na „strumieniu” danych wejściowych, ale rozumie je w kawałkach rozdzielanych znakami nowej linii. Jest to narzędzie uniksowe, co oznacza, że ​​robi jedną rzecz bardzo dobrze. Jedną rzeczą jest „praca nad plikiem liniowo”. Zmuszenie go do zrobienia czegoś innego będzie trudne i grozi błędem. Morał tej historii jest następujący: wybierz odpowiednie narzędzie. Wiele z twoich pytań wydaje się przybrać formę „Jak sprawić, by to narzędzie zrobiło coś, czego nigdy nie zamierzano?” Te pytania są interesujące, ale jeśli pojawią się w trakcie rozwiązywania prawdziwego problemu, prawdopodobnie robisz to źle.
dmckee --- były kot moderator

7
@JBBrown trto często pomijany klejnot do budowy rurociągów.
dmckee --- były moderator kociak

70
tr jest świetny, ale możesz zastąpić znaki nowej linii pojedynczymi znakami. Musisz użyć innego narzędzia, jeśli chcesz zastąpić znaki nowej linii ciągiem
Eddy

21
@Eddy - użyłem tr, aby zastąpić nowe wiersze znakiem, który nie pojawił się w tekście (użyłem backticka), a następnie sed, aby zastąpić backtick ciągiem, którego chciałem użyć
rjohnston 30.01.2013

493

Szybka odpowiedź

sed ':a;N;$!ba;s/\n/ /g' file
  1. : a utwórz etykietę „a”
  2. N. dodaj następny wiersz do obszaru wzorów
  3. $! jeśli nie ostatnia linia , ba gałąź (przejdź do) oznacz „a”
  4. s zamiennik , / \ n / regex dla nowej linii , / / spacją , / g dopasowanie globalne (tyle razy, ile to możliwe)

sed będzie przechodzić od kroku 1 do 3, aż dojdzie do ostatniej linii, dopasowując wszystkie linie do obszaru wzorów, gdzie sed zastąpi wszystkie \ n znaków


Alternatywy

Wszystkie alternatywy, w przeciwieństwie do sed , nie będą musiały dotrzeć do ostatniej linii, aby rozpocząć proces

z uderzeniem , powoli

while read line; do printf "%s" "$line "; done < file

z Perl , sed -jak prędkość

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

z tr , szybszy niż sed , można zastąpić tylko jedną postacią

tr '\n' ' ' < file

z wklejaniem , prędkość podobna do tr , można zastąpić tylko jedną postacią

paste -s -d ' ' file

z awk , tr- podobną prędkością

awk 1 ORS=' ' file

Inne alternatywy, takie jak „echo $ (<plik)”, są powolne, działają tylko na małych plikach i muszą przetworzyć cały plik, aby rozpocząć proces.


Długa odpowiedź od sed FAQ 5.10

5.10 Dlaczego nie mogę dopasować ani usunąć nowego wiersza za pomocą
sekwencji ucieczki \ n ? Dlaczego nie mogę dopasować 2 lub więcej linii za pomocą \ n?

\ N nigdy nie będzie pasować do nowej linii na końcu linii, ponieważ
nowa linia jest zawsze usuwana przed umieszczeniem linii w obszarze
wzorów. Aby wstawić 2 lub więcej linii do przestrzeni wzorów, użyj
polecenia „N” lub czegoś podobnego (takiego jak „H; ...; g;”).

Sed działa w ten sposób: sed odczytuje jedną linię na raz, odcina
kończącą się nową linię , umieszcza to, co pozostało w przestrzeni wzorcowej, w której
skrypt sed może ją adresować lub zmieniać, a gdy
drukowana jest przestrzeń wzorcowa, dodaje nową linię do standardowego wejścia (lub do pliku). Jeśli
przestrzeń wzoru zostanie całkowicie lub częściowo usunięta za pomocą „d” lub „D”,
nowa linia nie jest dodawana w takich przypadkach. Tak więc skrypty lubią

  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         

NIGDY nie zadziała, ponieważ końcowy znak nowej linii jest usuwany przed
wstawieniem linii do obszaru wzorów. Aby wykonać powyższe zadania,
użyj jednego z tych skryptów:

  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          

Ponieważ wersje sed inne niż GNU sed mają ograniczenia wielkości
bufora wzorców, należy tutaj preferować narzędzie uniksowe „tr”.
Jeśli ostatni wiersz pliku zawiera nową linię, GNU sed doda
tę nową linię do wyniku, ale usunie wszystkie inne, podczas gdy tr doda
usunie wszystkie nowe linie.

Aby dopasować blok dwóch lub więcej wierszy, istnieją 3 podstawowe opcje:
(1) użyj polecenia „N”, aby dodać następny wiersz do obszaru wzorów;
(2) użyj polecenia „H” co najmniej dwukrotnie, aby dołączyć bieżącą linię
do przestrzeni Hold, a następnie pobrać linie z przestrzeni Hold za
pomocą x, g lub G; lub (3) użyj zakresów adresów (patrz sekcja 3.3 powyżej),
aby dopasować linie między dwoma określonymi adresami.

Wybory (1) i (2)
wstawią \ n do przestrzeni wzorców, gdzie można ją adresować według potrzeb ('s / ABC \ nXYZ / alfabet / g'). Jeden przykład
użycia „N” do usunięcia bloku linii pojawia się w sekcji 4.13
(„Jak usunąć blok określonych kolejnych linii?”). Ten
przykład można zmodyfikować, zmieniając polecenie delete na coś
innego, na przykład „p” (drukuj), „i” (wstaw), „c” (zmiana), „a” (dołącz)
lub „s” (zamiennik) .

Choice (3) nie położy \ n do przestrzeni wzorca, ale nie
pasuje do bloku kolejnych wierszy, więc może się okazać, że nie masz
jeszcze potrzebne \ n, aby znaleźć to, czego szukasz. Ponieważ
wersja GNU sed 3.02.80 obsługuje teraz tę składnię:

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

oprócz tradycyjnych
adresów z zakresu „/ from here /, / to there / {...}” można całkowicie uniknąć użycia \ n.


6
trbył świetnym pomysłem, a ogólny zasięg zapewnia odpowiedź na najwyższym poziomie.
Nowa Aleksandria,

1
+1 za używanie ( standardowe narzędzie ) paste... i wszystkich innych!
Totor


4
Najlepsze w tej odpowiedzi jest to, że „długa odpowiedź” dokładnie wyjaśnia, w jaki sposób i dlaczego to polecenie działa.
pdwalker

3
To może być najbardziej pomocna z tysięcy odpowiedzi, które przeczytałem na stackexchange. Muszę dopasować wiele znaków w liniach. Żaden poprzedni przykład sed nie obejmował wielu linii, a tr nie może obsłużyć dopasowania wielu znaków. Perl wygląda dobrze, ale nie działa tak, jak się spodziewam. Głosowałbym na tę odpowiedź kilka razy, gdybym mógł.
mightypile

225

Krótsza alternatywa awk:

awk 1 ORS=' '

Wyjaśnienie

Program awk składa się z reguł składających się z bloków kodu warunkowego, tj .:

condition { code-block }

Jeśli kod blok zostanie pominięty, domyślnie jest używany: { print $0 }. Zatem 1interpretowany jest jako prawdziwy warunek i print $0jest wykonywany dla każdej linii.

Podczas awkodczytywania danych wejściowych dzieli je na rekordy na podstawie wartości RS(Separator rekordów), który domyślnie jest nowym awkwierszem , a zatem domyślnie parsuje dane wejściowe liniowo. Podział obejmuje również usunięcie RSdanych z rekordu wejściowego.

Teraz podczas drukowania rekordu ORSdołączany jest do niego (Output Record Separator), domyślnie jest to nowa linia. Zmieniając ORSspację, wszystkie znaki nowej linii są zamieniane na spacje.


5
Bardzo podoba mi się to proste rozwiązanie, które jest o wiele bardziej czytelne, niż inne
Fedir RYKHTIK

8
Jeśli ma to większy sens, można to efektywnie zapisać jako: awk 'BEGIN { ORS=" " } { print $0 } END { print "\n"} ' file.txt(dodanie końcowego wiersza tylko w celu zilustrowania początku / końca); „1” oznacza true(przetworzenie linii) i print(wydrukowanie linii). Do tego wyrażenia można również dodać warunkowy, np. Działający tylko na liniach pasujących do wzorca: awk 'BEGIN { ORS=" " } /pattern/ { print $0 } END { print "\n"} '
Michał

2
Możesz to zrobić więcej: codeawk 'ORS = ""' file.txtcode
Udi

Podczas korzystania z awk w ten sposób niestety usuwany jest również ostatni wiersz wiersza w pliku. Zobacz odpowiedź Patrick Dark powyżej na temat używania „tr” w podpowłoce, takiej jak „cat file | echo $ (tr "\ 012" ")" co załatwi sprawę. Fajne
Bernie Reiter

143

gnu sed ma opcję -zdla rekordów (wierszy) oddzielonych od siebie. Możesz po prostu zadzwonić:

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

4
Nawet jeśli dane wejściowe zawierają wartości null, zostaną zachowane (jako separatory rekordów).
Toby Speight

6
Czy to nie załaduje całego wejścia, jeśli nie ma wartości zerowych? W takim przypadku przetwarzanie pliku o pojemności wielu gigabajtów może być awarią.
Ruslan

3
@ Ruslan, tak, ładuje całe dane wejściowe. To rozwiązanie nie jest dobrym pomysłem w przypadku plików wielogigabajtowych.
JJoao,

7
To naprawdę najlepsza odpowiedź. Inne wyrażenia są zbyt zniekształcone, aby je zapamiętać. @JJoao Możesz go używać z -u, --unbuffered. Te manstany magiczne: „obciążenie minimalne ilości danych z plików wejściowych i przepłukać bufory wyjściowe częściej”.
not2qubit

więc. wiele. to.
sjas,

85

Wersja Perla działa w oczekiwany sposób.

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

Jak wskazano w komentarzach, warto zauważyć, że wprowadzono to zmiany. -i.bakda ci kopię zapasową oryginalnego pliku przed zastąpieniem, na wypadek gdyby twoje wyrażenie regularne nie było tak inteligentne, jak myślałeś.


23
Proszę przynajmniej wspomnieć, że -ibez przyrostka nie tworzy kopii zapasowej . -i.bakchroni cię przed łatwym, brzydkim błędem (powiedzmy, zapomnieniem -pwpisania i wyzerowaniem pliku).
Telemachus,

6
@Telemachus: To słuszna kwestia, ale można się z nią kłócić. Głównym powodem, o którym nie wspomniałem, jest to, że przykład sed w pytaniu PO nie tworzy kopii zapasowych, więc wydaje się tutaj zbędny. Innym powodem jest to, że tak naprawdę nigdy nie korzystałem z funkcji tworzenia kopii zapasowych (w rzeczywistości uważam, że automatyczne tworzenie kopii zapasowych jest denerwujące), więc zawsze zapominam o tym. Trzecim powodem jest to, że moja linia poleceń wydłuża się o cztery znaki. Na lepsze lub gorsze (prawdopodobnie gorsze) jestem kompulsywnym minimalistą; Po prostu wolę zwięzłość. Zdaję sobie sprawę, że się nie zgadzasz. Postaram się jak najlepiej zapamiętać, aby ostrzegać przed kopiami zapasowymi w przyszłości.
ire_and_curses

6
@Ire_and_curses: Właściwie to byłeś cholernie dobrym argumentem za ignorowaniem mnie. To znaczy, masz powody swoich wyborów i bez względu na to, czy zgadzam się z nimi, z pewnością to szanuję. Nie jestem do końca pewien, dlaczego, ale ostatnio miałem łzy nad tą konkretną rzeczą ( -iflaga w Perlu bez sufiksu). Jestem pewien, że wkrótce znajdę coś innego do obsesji. :)
Telemachus

Naprawdę niefortunne jest to, że nie działa to ze standardowym podaniem -nazwy pliku. Czy jest na to sposób? To mój sposób, aby nie martwić się o modyfikację pliku, używając potoku rozpoczynającego się od cat.
Steven Lu

@StevenLu Perl domyślnie czyta ze STDIN, jeśli nie podano nazw plików. Więc możesz zrobić np.perl -i -p -e 's/\n//' < infile > outfile
ire_and_curses

44

Kto potrzebuje sed? Oto bashsposób:

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

2
Upvote, zwykle użyłem najwyższej odpowiedzi, ale kiedy przesyłam przez nią / dev / urandom, sed nie będzie drukował, dopóki EOF, a ^ C nie będzie EOF. To rozwiązanie drukuje za każdym razem, gdy widzi nową linię. Dokładnie to, czego potrzebowałem! Dzięki!
Wasilij Sharapov,

1
to dlaczego nie: echo -n `cat days.txt` Z tego postu
Tony

9
@ Tony, ponieważ backticks są przestarzałe, a kot jest zbędny ;-) Użyj: echo $ (<days.txt)
seumasmac

10
Nawet przy użyciu cat: while read line; do echo -n "$line "; done < test.txt. Może się przydać, jeśli problem stanowi podpowłoka.
Carlo Cannas,

5
echo $(<file)wyciska wszystkie białe znaki na jedną spację, nie tylko znaki nowej linii: wykracza to poza to, o co prosi OP.
glenn jackman

27

Aby zastąpić wszystkie nowe wiersze spacjami za pomocą awk, bez wczytywania całego pliku do pamięci:

awk '{printf "%s ", $0}' inputfile

Jeśli chcesz ostatnią nową linię:

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

Możesz użyć znaku innego niż spacja:

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

END{ print ""}jest krótszą alternatywą dla nowej linii końcowej.
Izaak

22
tr '\n' ' ' 

to polecenie.

Prosty i łatwy w użyciu.


14
lub po prostu tr -d '\n'jeśli nie chcesz dodawać spacji
spuder

21

Trzy rzeczy.

  1. tr(lub catitp.) absolutnie nie jest potrzebne. (GNU) sedi (GNU) awk, w połączeniu, mogą wykonać 99,9% dowolnego przetwarzania tekstu, którego potrzebujesz.

  2. stream! = oparty na linii. edto edytor liniowy. sednie jest. Zobacz wykład sed , aby uzyskać więcej informacji na temat różnicy. Większość ludzi myli się sedz tym, że opiera się na liniach, ponieważ domyślnie nie jest zbyt chciwy w dopasowywaniu wzorców dla dopasowań SIMPLE - na przykład podczas wyszukiwania wzorców i zastępowania jednym lub dwoma znakami domyślnie zastępuje tylko przy pierwszym dopasowaniu znajduje (chyba że globalne polecenie określiło inaczej). Nie byłoby nawet globalnego polecenia, gdyby opierało się ono na liniach, a nie na STREAM, ponieważ oceniałoby tylko linie na raz. Spróbuj uruchomić ed; zauważysz różnicę. edjest dość przydatny, jeśli chcesz iterować po określonych liniach (np. w pętli for), ale w większości przypadków po prostu chcesz sed.

  3. Biorąc to pod uwagę,

    sed -e '{:q;N;s/\n/ /g;t q}' file
    

    działa dobrze w GNU w sedwersji 4.2.1. Powyższe polecenie zastąpi wszystkie znaki nowej linii spacjami. Pisanie jest brzydkie i trochę kłopotliwe, ale działa dobrze. W {}„s można pominąć, ponieważ są one zawarte wyłącznie w celach Sanity.


3
Jako osoba, która wie tylko tyle sed, aby zrobić podstawowe rzeczy, muszę powiedzieć, że jest bardziej niż o tym, co można zrobić z sedraczej jak łatwo jest zrozumieć, co się dzieje. Bardzo ciężko mi się pracuje, sedwięc wolę prostsze polecenie, kiedy mogę z niego korzystać.
Nate

Używanie t qjako skoku warunkowego działa ze wzorem podobnym s/\n / /do (łączenie wszystkich linii rozpoczynających się spacją) bez wczytywania całego pliku do pamięci. Przydatne podczas przekształcania plików o wielkości wielu megabajtów.
tekstowe

Artykuł, który podlinkowałeś, nie odzwierciedla tego, co mówisz
hek2mgl,

Jest to prawie 800 razy wolniej niż akceptowana odpowiedź przy dużym nakładzie. Wynika to z działania zastępczego dla każdego wiersza przy coraz większych wejściach.
Thor

13

Odpowiedź z: etykietą ...

Jak mogę zastąpić znak nowej linii (\ n) za pomocą sed?

... nie działa we Freebsd 7.2 w linii poleceń:

(echo foo; pasek echa) | sed ': a; N; $! ba; s / \ n / / g'
sed: 1: ": a; N; $! ba; s / \ n / / g": nieużywana etykieta „a; N; $! ba; s / \ n / / g”
bla
bar

Ale dzieje się tak, jeśli umieścisz skrypt sed w pliku lub użyjesz -e, aby „zbudować” skrypt sed ...

> (echo foo; pasek echa) | sed -e: a -e N -e '$! ba' -e 's / \ n / / g'
bar foo

lub ...

> cat > x.sed << eof
:a
N
$!ba
s/\n/ /g
eof

> (echo foo; echo bar) | sed -f x.sed
foo bar

Może sed w OS X jest podobny.


Szereg argumentów -e zadziałał dla mnie w systemie Windows przy użyciu MKS! Dzięki!
JamesG

12

Łatwe do zrozumienia rozwiązanie

Miałem ten problem. Kickerem było to, że potrzebowałem rozwiązania do pracy na BSD (Mac OS X) i GNU (Linux i Cygwin ) sedoraz tr:

$ echo 'foo
bar
baz


foo2
bar2
baz2' \
| tr '\n' '\000' \
| sed 's:\x00\x00.*:\n:g' \
| tr '\000' '\n'

Wynik:

foo
bar
baz

(ma końcowy znak nowej linii)

Działa w systemach Linux, OS X i BSD - nawet bez obsługi UTF-8 lub z kiepskim terminalem.

  1. Użyj, traby zamienić znak nowej linii na inny znak.

    NULL (\000 lub \x00) jest fajny, ponieważ nie wymaga obsługi UTF-8 i prawdopodobnie nie będzie używany.

  2. Posługiwać się sed aby dopasowaćNULL

  3. Użyj, traby zamienić dodatkowe nowe linie, jeśli ich potrzebujesz


1
Subtelna uwaga na temat nomenklatury: znak ten \000jest powszechnie określany jako NUL(jeden L) i NULLjest ogólnie używany, gdy mówi się o zerowym wskaźniku (w C / C ++).
sqweek


9

Nie jestem ekspertem, ale myślę sed, że najpierw musisz dołączyć następną linię do przestrzeni wzorów, bij używając „ N”. Z sekcji „Multiline Pattern Space” w „Advanced sed Commands” książki sed & awk (Dale Dougherty i Arnold Robbins; O'Reilly 1997; strona 107 w podglądzie ):

Polecenie Dalej (N) multilinii tworzy wielowierszową przestrzeń wzorcową, czytając nowy wiersz danych wejściowych i dołączając go do zawartości przestrzeni wzorcowej. Oryginalna zawartość przestrzeni wzorów i nowa linia wprowadzania są oddzielone nową linią. Osadzony znak nowej linii można dopasować we wzorach sekwencją zmiany znaczenia „\ n”. W wielowierszowej przestrzeni wzorca metaznak „^” odpowiada pierwszemu znakowi w przestrzeni wzorca, a nie znakowi (znakom) po dowolnej osadzonej nowej linii. Podobnie „$” pasuje tylko do końcowej nowej linii w obszarze wzorców, a nie do żadnych osadzonych nowej linii. Po wykonaniu następnego polecenia kontrola jest następnie przekazywana do kolejnych poleceń w skrypcie.

Od man sed:

[2addr] N

Dołącz następny wiersz danych wejściowych do obszaru wzorców, używając osadzonego znaku nowej linii, aby oddzielić dołączony materiał od oryginalnej zawartości. Zauważ, że bieżący numer linii zmienia się.

Mam używany ten szukać (wielokrotność) źle sformatowane pliki dziennika, w których ciąg wyszukiwania można znaleźć na „osierocone” następnym wierszu.


7

Zastosowałem podejście hybrydowe, aby ominąć kwestię nowej linii, używając tr do zastąpienia nowej linii tabulatorami, a następnie zamiany tabulacji na cokolwiek chcę. W tym przypadku „
”, ponieważ próbuję generować podziały HTML.

echo -e "a\nb\nc\n" |tr '\n' '\t' | sed 's/\t/ <br> /g'`

6

W odpowiedzi na powyższe rozwiązanie „tr”, w systemie Windows (prawdopodobnie przy użyciu wersji tr Gnuwin32), proponowane rozwiązanie:

tr '\n' ' ' < input

nie działało dla mnie, albo z jakiegoś powodu albo błąd, albo faktycznie zastąpi \ nw / ''.

Używając innej funkcji tr, opcja „usuń” -d działała jednak:

tr -d '\n' < input

lub „\ r \ n” zamiast „\ n”


3
W systemie Windows prawdopodobnie musisz użyć tr "\n" " " < input. Powłoka systemu Windows (cmd.exe) nie traktuje apostrofu jako cudzysłowu.
Keith Thompson

Nie, w podsystemie Ubuntu systemu Windows 10 musisz użyćtr "\n\r" " " < input.txt > output.txt
użytkownik1491819,

To działa na Windows 10 przy użyciu GnuWin32: cat SourceFile.txt | tr --delete '\r\n' > OutputFile.txt. Lub zamiast Gnuwin32 użyj Gow (Gnu na Windows), github.com/bmatzelle/gow/wiki
Alchemistmatt

5

Rozwiązanie kuloodporne. Bezpieczny dla danych binarnych i zgodny z POSIX, ale powolny.

POSIX sed wymaga danych wejściowych zgodnie z plikiem tekstowym POSIX i definicjami linii POSIX , więc bajty NULL i zbyt długie linie nie są dozwolone, a każda linia musi kończyć się nową linią (w tym ostatnią linią). Utrudnia to używanie sed do przetwarzania dowolnych danych wejściowych.

Poniższe rozwiązanie pozwala uniknąć sed i zamiast tego konwertuje bajty wejściowe na kody ósemkowe, a następnie ponownie na bajty, ale przechwytuje kod ósemkowy 012 (nowa linia) i wyświetla zamiast niego ciąg zastępczy. O ile wiem, rozwiązanie jest zgodne z POSIX, więc powinno działać na wielu różnych platformach.

od -A n -t o1 -v | tr ' \t' '\n\n' | grep . |
  while read x; do [ "0$x" -eq 012 ] && printf '<br>\n' || printf "\\$x"; done

Dokumentacja referencyjna POSIX: sh , język poleceń powłoki , od , tr , grep , read , [ , printf .

Obaj read, [i printfsą Zabudowy w co najmniej bash, ale to prawdopodobnie nie jest gwarantowana przez POSIX, więc na niektórych platformach może być, że każdy bajt wejściowy rozpocznie jeden lub więcej nowych procesów, które będą spowolnić. Nawet w trybie bash to rozwiązanie osiąga jedynie około 50 kB / s, więc nie nadaje się do dużych plików.

Testowane na Ubuntu (bash, dash i busybox), FreeBSD i OpenBSD.


5

W niektórych sytuacjach możesz zmienić RSna inny ciąg lub znak. W ten sposób \ n jest dostępny dla sub / gsub:

$ gawk 'BEGIN {RS="dn" } {gsub("\n"," ") ;print $0 }' file

Moc skryptowania powłoki polega na tym, że jeśli nie wiesz, jak to zrobić w jeden sposób, możesz to zrobić w inny sposób. I wiele razy masz więcej rzeczy do wzięcia pod uwagę niż skomplikowane rozwiązanie prostego problemu.

Jeśli chodzi o to, że gawk jest powolny ... i wczytuje plik do pamięci, nie wiem tego, ale dla mnie gawk wydaje się działać z jedną linią na raz i jest bardzo bardzo szybki (nie tak szybki jak niektóre inne , ale czas pisania i testowania również się liczy).

Przetwarzam MB, a nawet GB danych, a jedynym ograniczeniem, jakie znalazłem, jest rozmiar linii.


5

Jeśli jesteś nieszczęśliwy na tyle, aby poradzić sobie z zakończeniami linii systemu Windows, musisz usunąć \ri\n

tr '[\r\n]' ' ' < $input > $output

Zastępuje [to spacją i \rspacją oraz \nspacją i ]spacją.tr -d '\r\n' <fileusunie dowolne znaki \rlub \nznaki, ale nie o to też pyta. tr -d '\r' <fileusunie wszystkie \rpostacie (bez względu na to, czy sąsiadują \n), co jest prawdopodobnie bliższe przydatności, a także prawdopodobnie poprawne dla potrzeb PO (wciąż zakładając, że trrozumiesz tę notację odwrotnego ukośnika).
tripleee

4

Możesz użyć xargs- \ndomyślnie zastąpi go spacją.

Miałoby to jednak problemy, gdyby dane wejściowe zawierały jakiś przypadek unterminated quote, np. Jeśli znaki cudzysłowu w danym wierszu nie pasują.


xargs ładnie obsługuje również ostatnią linię:
AAAfarmclub

4

Znajduje i zamienia za pomocą zezwolenia \ n

sed -ie -z 's/Marker\n/# Marker Comment\nMarker\n/g' myfile.txt

Znacznik

Staje się

# Komentarz znacznika

Znacznik


4

Dlaczego nie znalazłem prostego rozwiązania awk?

awk '{printf $0}' file

printf wypisze każdą linię bez nowych linii, jeśli chcesz oddzielić oryginalne linie spacją lub inną:

awk '{printf $0 " "}' file

echo "1\n2\n3" | awk '{printf $0}', to działa dla mnie. @ edi9999
Itachi

Masz rację przepraszam, zapomniałem fw printf
edi9999

to było jedyne podejście, które działało dla mnie w ramach git bash dla Windows
Plato

3

W systemie Mac OS X (za pomocą sed FreeBSD):

# replace each newline with a space
printf "a\nb\nc\nd\ne\nf" | sed -E -e :a -e '$!N; s/\n/ /g; ta'
printf "a\nb\nc\nd\ne\nf" | sed -E -e :a -e '$!N; s/\n/ /g' -e ta


3

Za pomocą Awk:

awk "BEGIN { o=\"\" }  { o=o \" \" \$0 }  END { print o; }"

2
Nie musisz uciekać przed cudzysłowami i znakiem dolara, jeśli zmienisz zewnętrzne na pojedyncze cudzysłowy. Litera „o” jest zwykle uważana za zły wybór jako nazwa zmiennej, ponieważ można ją pomylić z cyfrą „0”. Nie musisz także inicjować swojej zmiennej, domyślnie jest to ciąg zerowy. Jednakże, jeśli nie chce się obcych wiodącą miejsca: awk '{s = s sp $0; sp = " "} END {print s}'. Jednak zobacz moją odpowiedź na sposób użycia awk bez wczytywania całego pliku do pamięci.
Wstrzymano do odwołania.

Proszę sprawdzić odpowiedź Thora zamiast. Jest to sposób bardziej efektywny, czytelny i po prostu lepiej za wszelką cenę w porównaniu tego podejścia (chociaż to będzie działać)!
mschilli,

Koleś, rozumiem. Nie muszę przecierać jej w twarz :-) W każdym razie odpowiedź Thora znajduje się wysoko nad stroną (co jest słuszne), więc co cię to obchodzi?
kralyk

3

Rozwiązaniem, które szczególnie mi się podoba, jest dodanie całego pliku w przestrzeni wstrzymania i zastąpienie wszystkich nowych linii na końcu pliku:

$ (echo foo; echo bar) | sed -n 'H;${x;s/\n//g;p;}'
foobar

Jednak ktoś powiedział mi, że przestrzeń wstrzymania może być skończona w niektórych implementacjach sed.


1
zastąpienie pustym ciągiem w twojej odpowiedzi ukrywa fakt, że zawsze użycie H do dołączenia do przestrzeni wstrzymania oznacza, że ​​przestrzeń wstrzymania rozpocznie się od nowej linii. Aby tego uniknąć, musisz użyć1h;2,$H;${x;s/\n/x/g;p}
Jeff

3

Zastąp nowy znak dowolnym ciągiem i zastąp także ostatni nowy znak

Czyste trrozwiązania można zastąpić tylko jednym znakiem, a czyste sedrozwiązania nie zastępują ostatniego nowego wiersza danych wejściowych. Poniższe rozwiązanie rozwiązuje te problemy i wydaje się bezpieczne dla danych binarnych (nawet w przypadku ustawień regionalnych UTF-8):

printf '1\n2\n3\n' |
  sed 's/%/%p/g;s/@/%a/g' | tr '\n' @ | sed 's/@/<br>/g;s/%a/@/g;s/%p/%/g'

Wynik:

1<br>2<br>3<br>

Jest to złe, ponieważ spowoduje niechciane wyjście na każdym wejściu zawierającym@
Steven Lu

@StevenLu: Nie, @na wejściu jest OK. Ucieka %ai wraca. Rozwiązanie może jednak nie być w pełni zgodne z POSIX (bajty NULL są niedozwolone, więc nie są dobre dla danych binarnych, a wszystkie wiersze muszą kończyć się znakiem nowej linii, aby dane trwyjściowe nie były naprawdę poprawne).
Håkon A. Hjortland

Ach Widzę, że to naprawiłeś. Trochę zawiłe, co powinno być prostą operacją, ale dobrą pracą.
Steven Lu

3

To sed wprowadza nowe linie po „normalnym” podstawieniu. Najpierw przycina znak nowej linii, następnie przetwarza zgodnie z instrukcjami, a następnie wprowadza nową linię.

Za pomocą sed możesz zastąpić „koniec” linii (nie znak nowej linii) po przycięciu, wybranym ciągiem, dla każdej linii wejściowej; ale sed wyświetli różne linie. Załóżmy na przykład, że chcesz zamienić „koniec linii” na „===” (bardziej ogólne niż zastąpienie pojedynczą spacją):

PROMPT~$ cat <<EOF |sed 's/$/===/g'
first line
second line
3rd line
EOF

first line===
second line===
3rd line===
PROMPT~$

Aby zastąpić znak nowej linii ciągiem, możesz nieefektywnie użyć tr , jak wskazano wcześniej, aby zastąpić znaki nowej linii „specjalnym znakiem”, a następnie użyć sed aby zastąpić ten znak specjalny ciągiem, który chcesz .

Na przykład:

PROMPT~$ cat <<EOF | tr '\n' $'\x01'|sed -e 's/\x01/===/g'
first line
second line
3rd line
EOF

first line===second line===3rd line===PROMPT~$

3

Możesz także użyć tej metody

sed 'x;G;1!h;s/\n/ /g;$!d'

Wyjaśnienie

x   - which is used to exchange the data from both space (pattern and hold).
G   - which is used to append the data from hold space to pattern space.
h   - which is used to copy the pattern space to hold space.
1!h - During first line won't copy pattern space to hold space due to \n is
      available in pattern space.
$!d - Clear the pattern space every time before getting next line until the
      last line.

Przepływ:
Gdy pierwszy wiersz pobierze z wejścia, następuje zamiana, więc 1 przechodzi do miejsca wstrzymania i \ n przychodzi do obszaru wzorców, następnie dołącza miejsce wstrzymania do obszaru wzorców, a następnie wykonuje się podstawienie i usuwa obszar wzorców.
Podczas dokonywania wymiany drugiej linii 2 przechodzi do przestrzeni wstrzymania, a 1 przychodzi do przestrzeni wzorców, następnie Gdołącza przestrzeń wstrzymania do przestrzeni wzorców, a następnie hkopiuje do niej wzór, a następnie zastępuje i usuwa. Ta operacja jest kontynuowana aż do osiągnięcia eof, a następnie wydrukuj dokładny wynik.


Ostrzegamy jednak, że to echo 'Y' | sed 'x;G;1!h;s/\n/X/g;$!d'skutkuje XY.
Spooky

3

Innym GNU sed metoda, prawie tak samo jak Zsolt Botykai „s odpowiedzi , ale ta wykorzystuje sed” mniej często wykorzystywane s y( Transliterate poleceń, co oszczędza) jeden bajt kodu (tylnym g):

sed ':a;N;$!ba;y/\n/ /'

Można by mieć nadzieję, że ydziałałby szybciej niż s(być może przy trprędkościach, 20x szybciej), ale w GNU sed v4.2.2 y jest o około 4% wolniejszy niż s.


Bardziej przenośna wersja BSD sed :

sed -e ':a' -e 'N;$!ba' -e 'y/\n/ /'

2
Z BSD sed yjest o około 15% szybszy. Zobacz tę odpowiedź na działający przykład.
Thor

Ponadto w przypadku BSD komendy sed muszą kończyć się po etykiecie, więc sed -e ':a' -e 'N;$!ba' -e 'y/\n/ /'byłby to właściwy sposób.
ghoti
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.