Czy sed może zastąpić nowe znaki wiersza?

42

Czy występuje problem ze znakiem sed i nową linią?
Mam plik test.txt z następującą zawartością

aaaaa  
bbbbb  
ccccc  
ddddd  

Następujące nie działa:
sed -r -i 's/\n/,/g' test.txt

Wiem, że mogę trz tego skorzystać, ale moje pytanie brzmi: dlaczego wydaje się to niemożliwe z sedem.

Jeśli jest to efekt uboczny przetwarzania pliku linia po linii, byłbym zainteresowany, dlaczego tak się dzieje. Myślę, że grepusuwa nowe linie. Czy sed robi to samo?

Jim
źródło
1
W takim przypadku sed może nie być najlepszym narzędziem do użycia (np. „Tr”). Istnieją narzędzia, które są bardziej intuicyjne, łatwiejsze do odczytania / konserwacji, działają lepiej (szczególnie w przypadku dużych zbiorów danych) itp. ... Nie używaj młotka do wkręcania śrub (nawet jeśli działa). Porównanie można znaleźć na: http://slash4.de/blog/python/sed-replace-newline-or-python-awk-tr-perl-xargs.html
omoser
2
trdodałby znak końca ,i wyprowadziłby niezakończoną linię. Najlepiej użyć pastezamiast tego:paste -sd , test.txt
Stéphane Chazelas,

Odpowiedzi:

48

Z GNU sedi pod warunkiem POSIXLY_CORRECTnie ma go w środowisku (dla wprowadzania jednowierszowego):

sed -i ':a;N;$!ba;s/\n/,/g' test.txt

Od https://stackoverflow.com/questions/1251999/sed-how-can-i-replace-a-newline-n :

  1. utwórz etykietę przez :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, aby nie robić tego w ostatnim wierszu (ponieważ powinna istnieć jedna końcowa nowa linia)).
  4. w końcu podstawienie zastępuje każdy nowy wiersz przecinkiem w obszarze wzorców (który jest całym plikiem).
Anthon
źródło
To wydaje się wskazywać, że problem polega na tym, że sed czyta wiersz po wierszu, ale nie rozumiem, dlaczego jest to problem. Mogłabym po prostu odczytać wiersz i zastąpić nowy znak wiersza (lub ostatni znak) znakiem,
Jim
1
@ jim Wygląda na to, że nie ma go w buforze do dopasowania, ale nie jestem biegły w sed, może ktoś inny może rzucić na to światło. Myślę, że powinieneś rozszerzyć swoje Q o te konkretne informacje, aby ludzie chętniej je czytali i mam nadzieję, że odpowiedzą.
Anthon
Powoduje toba: Event not found
krb686
@ krb686 Do czego odnosi się „To”? Czy uruchomiłeś powyższe sedpolecenie z tymi dokładnymi opcjami? W jakim test.txt pliku? Z którą wersją sed(try sed --version)?
Anthon
@Anthon Przepraszam, myślę, że chciałem powiedzieć „the”. Przeczytałem inny post SO, który poinformował mnie, że csh wymaga ode mnie ucieczki !. Co ciekawe, to wciąż nie działało dla mnie i ostatecznie musiałem podwójnie uciec z !mojego .cshskryptu. W tej chwili tak naprawdę nie mam problemu, ale czy wiesz, dlaczego tak się dzieje? Dla mnie sed :a;N;$\\!ba;s/\n/ /g'
zadziałało
17

Działa to z GNU sed:

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

-z jest uwzględnione od 4.2.2

NB. -zzmienia separator na znaki puste ( \0). Jeśli dane wejściowe nie zawierają żadnych znaków zerowych, całe dane wejściowe są traktowane jako pojedynczy wiersz. Może to wynikać z jego ograniczeń .

Aby uniknąć zastąpienia nowego wiersza ostatniego wiersza, możesz go zmienić z powrotem:

sed -z 's/\n/,/g;s/,$/\n/'

(Która jest sedponownie składnią GNU , ale to nie ma znaczenia, ponieważ całość jest tylko GNU)

Hielke Walinga
źródło
3
Zastąpi to także końcowy znak nowej linii, który może nie być tym, czego chce OP ... porównaj wynik z rozwiązaniem mikeserv .
don_crissti
7

Ze strony internetowej Oracle:

Narzędzie sed działa poprzez sekwencyjny odczyt pliku, wiersz po wierszu, do pamięci. Następnie wykonuje wszystkie akcje określone dla linii i umieszcza linię z powrotem w pamięci, aby zrzucić do terminala z wprowadzonymi żądanymi zmianami. Po wykonaniu wszystkich działań w tym jednym wierszu, odczytuje on następny wiersz pliku i powtarza proces aż do zakończenia pliku.

Zasadniczo oznacza to, że ponieważ sed czyta wiersz po wierszu, znak nowej linii nie jest dopasowany.

Rozwiązanie z https://stackoverflow.com/questions/1251999/sed-how-can-i-replace-a-newline-n to:

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

lub, w wersji przenośnej (bez ;łączenia po etykietach znaczników skoku)

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

Wyjaśnienie, jak to działa, znajduje się na tej stronie.

użytkownik204992
źródło
Użyłem tego zmodyfikowanego formularza do analizy dzienników VPN i umieszczenia „uwierzytelnionego” użytkownika oraz informacji o znaczniku czasu w tej samej linii. Twoje zdrowie!
user208145,
Zauważ, że ta składnia jest specyficzna dla GNU i nawet w GNU sed, jeśli POSIXLY_CORRECT znajduje się w środowisku, a wejście ma tylko jedną linię, nie będzie żadnych danych wyjściowych.
Stéphane Chazelas
5

sedzawsze usuwa końcowy \newline tuż przed zapełnieniem przestrzeni wzorców, a następnie dołącza jeden przed wypisaniem wyników skryptu. \nEwline można było w strukturze przestrzeni na różne sposoby - ale nigdy, jeśli nie jest wynikiem zmiany. Jest to ważne - \newline w sedprzestrzeni wzorów zawsze odzwierciedla zmianę i nigdy nie występuje w strumieniu wejściowym. \newline to jedyny ogranicznik, na który sedder może liczyć przy nieznanych danych wejściowych.

Jeśli chcesz zastąpić wszystkie \newline przecinkami, a plik nie jest zbyt duży, możesz:

sed 'H;1h;$!d;x;y/\n/,/'

To dodaje każdą linię wejściową do hstarej spacji - z wyjątkiem pierwszej, która zamiast tego zastępuje hstarą spację - po \nznaku ewline. Następnie dusuwa każdy wiersz nie $!ostatni z wyniku. W ostatnim wierszu Hstare i wzorce są xzmieniane, a wszystkie \nznaki ewline są y///tłumaczone na przecinki.

W przypadku dużych plików taka sytuacja z sedpewnością spowoduje problemy - bufor na granicach linii, który można łatwo przepełnić tego rodzaju działaniami.

mikeserv
źródło
2

Alternatywnie możesz użyć nieco prostszej składni:

sed ':a;N;s/\n/,/g;ba'

... tylko zmieniam kolejność sekwencji.

Rodec
źródło
3
Ale uruchamia spolecenie dla każdego wiersza wejściowego na przestrzeni wzorów, która jest coraz większa.
Stéphane Chazelas,
1

Jest tu bardzo ładna magia sed . I kilka dobrych uwag dotyczących przepełnienia przestrzeni wzorów. Uwielbiam używać sed, nawet jeśli nie jest to najprostszy sposób, ponieważ jest tak kompaktowy i mocny. Ma to jednak swoje ograniczenia, a dla dużych ilości danych przestrzeń wzorców musiałaby być mahoosive.

GNU mówi to:

Dla tych, którzy chcą pisać przenośne skrypty sed, należy pamiętać, że niektóre implementacje ograniczają długość linii (dla wzorca i spacji) do nie więcej niż 4000 bajtów. Standard posix określa, że ​​implementacje zgodne z sedem powinny obsługiwać co najmniej 8192 bajtów długości linii. GNU sed nie ma wbudowanego ograniczenia długości linii; tak długo, jak może malloc () więcej pamięci (wirtualnej), możesz karmić lub konstruować linie tak długo, jak chcesz.
Jednak rekurencja służy do obsługi wzorców i nieokreślonego powtarzania. Oznacza to, że dostępna przestrzeń stosu może ograniczyć rozmiar bufora, który może być przetwarzany przez określone wzorce.

Nie mam wiele do dodania, ale chciałbym skierować cię w stronę mojego przewodnika po sed . To jest świetne. http://www.grymoire.com/Unix/Sed.html

a oto moje rozwiązanie:

for i in $(cat test.txt); do echo -n $i','; done; echo '' >> somewhere

dobrze to działa

Xeuari
źródło
-1

Powiedzmy, że chcesz zastąpić znaki nowej linii \n. Chciałem to zrobić, więc oto co zrobiłem:

(echo foo; echo bar; echo baz) | sed -r '$!s/$/\\n/' | tr -d '\n' 
# Output: foo\nbar\nbaz

Oto, co robi: dla wszystkich linii z wyjątkiem ostatniej , dołącz \n. Następnie usuń nowe linie za pomocą tr.

Camilo Martin
źródło
-rjest dostępny tylko w GNU sed, a nie BSD.
kenorb