Jak wstawić nową linię przed wzorem?


138

Jak wstawić nową linię przed wzorcem w linii?

Na przykład spowoduje to wstawienie nowej linii za wzorcem wyrażenia regularnego.

sed 's/regex/&\n/g'

Jak mogę zrobić to samo, ale przed wzorem?

Biorąc pod uwagę ten przykładowy plik wejściowy, wzorcem do dopasowania jest numer telefonu.

some text (012)345-6789

Powinno stać się

some text
(012)345-6789

Powtarzam tylko odpowiedź na pytanie Jak wstawić nowy wiersz / podział wiersza po wierszu za pomocą seda :sed '/regex/G'
Adam Schmideg

1
@NilsvonBarth, dlaczego proste pytanie jest złym pytaniem?
Josh

Odpowiedzi:


177

Działa to w bashi zsh, przetestowane w systemie Linux i OS X:

sed 's/regexp/\'$'\n/g'

Ogólnie rzecz biorąc, for, $po którym następuje literał ciągu w pojedynczych cudzysłowach, bashwykonuje podstawienie odwrotnym ukośnikiem w stylu C, np. $'\t'Jest tłumaczone na literał tabulacji. Dodatkowo sed chce, aby twój literał nowej linii był chroniony przed odwrotnym ukośnikiem, stąd \poprzednia $. I wreszcie, sam znak dolara nie powinien być cytowany, aby był interpretowany przez powłokę, dlatego zamykamy cudzysłów przed znakiem $i otwieramy go ponownie.

Edycja : zgodnie z sugestią w komentarzach @ mklement0, działa to również:

sed $'s/regexp/\\\n/g'

Oto co się tutaj dzieje: całe polecenie sed jest teraz napisem w stylu C, co oznacza, że ​​odwrotny ukośnik, którego sed wymaga, aby został umieszczony przed literałem nowego wiersza, powinien być teraz poprzedzony innym lewym ukośnikiem. Chociaż bardziej czytelne, w tym przypadku nie będziesz w stanie dokonać podstawień łańcucha powłoki (bez ponownego uczynienia go brzydkim).


7
To daje mi "niezmieniony znak nowej linii wewnątrz zastępczego wzorca" na OSX.
Matt Gibson,

@Matt Gibson to bardzo dziwne, ponieważ „znak nowej linii bez znaku zmiany znaczenia” jest podawany tylko wtedy, gdy masz prawdziwy znak nowej linii bez odwrotnego ukośnika we wzorcu podstawiania. Mój kod powyżej działa w rzeczywistości również w niektórych innych powłokach, np. Zsh, ksh.
mojuba

3
@Matt Gibson ... lub jeśli zapomnisz ukośnika przed '$' \ nw moim kodzie.
mojuba

7
Jak napisano, te wyrażenia całkowicie zastępują wyrażenie regularne nowym wierszem, zamiast wstawiać nowy wiersz w środku istniejącego wiersza zgodnie z żądaniem. Oto jak kiedyś zmodyfikowaną formę tej odpowiedzi, aby wstawić znak nowej linii pomiędzy dwoma dopasowanych wzorców: sed '\(first match\)\(second match\)/\1\'$'\n''\2/g'. Zwróć uwagę na dwa pojedyncze cudzysłowy po \ n. Pierwsza zamyka $sekcję „ ”, tak że nie ma wpływu na pozostałą część linii. Bez tych cudzysłowów \ 2 został zignorowany.
David Ravetti

12
Inną opcją jest użycie jednego ANSI C-cudzysłowie : sed $'s/regexp/\\\n/g', co poprawia czytelność - jedyne zastrzeżenie jest to, że wtedy trzeba podwoić wszystkie dosłownych \ znaków.
mklement0

43

Niektóre inne odpowiedzi nie działały w mojej wersji seda. Zmiana pozycji &i \nzadziałała.

sed 's/regexp/\n&/g' 

Edycja: Wydaje się, że to nie działa w systemie OS X, chyba że zainstalujesz gnu-sed.


9
Nie jestem pewien, czy to działa we wszystkich wersjach seda. Wypróbowałem to na moim Macu i \ n otrzymało wyjście jako „n”
Todd Gamblin

3
Spędziłem 15 minut na komputerze Mac w pracy, zanim przeczytałem Twoją odpowiedź. Idź Apple!
Rick77

1
Dla osób korzystających z homebrew: brew install gnu-sednastępniegsed 's/regexp/\n&/g'
aaaaaa

1
... a następnieecho 'alias sed=gsed' >> ~/.bashrc
Proximo

36

W sedzie nie można łatwo dodawać nowych linii do strumienia wyjściowego. Musisz użyć linii kontynuacji, co jest niezręczne, ale działa:

$ sed 's/regexp/\
&/'

Przykład:

$ echo foo | sed 's/.*/\
&/'

foo

Zobacz tutaj po szczegóły. Jeśli chcesz czegoś mniej niezręcznego, możesz spróbować użyć perl -pez grupami dopasowań zamiast sed:

$ echo foo | perl -pe 's/(.*)/\n$1/'

foo

$1 odnosi się do pierwszej dopasowanej grupy w wyrażeniu regularnym, gdzie grupy są podane w nawiasach.


Dlaczego mówisz, że nie możesz dodawać nowych linii? Możesz po prostu zrobić sed 's / regexp / & \ n / g' To wszystko
Andres

2
To najmniej zhackowana rzecz, jaką możesz zrobić na Macu, aby wstawić nową linię (\ n nie działa na
Macu

Wersja Perla może zostać zmodyfikowana w celu edycji w miejscuperl -pi -e 's/(.*)/\n$1/' foo
tytułowy

2
@Andres: (przeważnie) implementacje Sed tylko dla funkcji POSIX, takie jak wersja BSD, która jest również dostarczana z OS X, nie obsługują sekwencji ucieczki znaków sterujących w części zastępującej swywołania funkcji (w przeciwieństwie do implementacji GNU Sed, która tak robi) . Powyższa odpowiedź działa w przypadku obu implementacji; przegląd wszystkich różnic znajduje się tutaj .
mklement0

29

Na moim Macu następujące wstawia pojedyncze „n” zamiast nowej linii:

sed 's/regexp/\n&/g'

To zastępuje nową linią:

sed "s/regexp/\\`echo -e '\n\r'`/g"

Robiłem edycję bezpośrednią sed -i '' -e ...i miałem problemy z ^Mdaszkiem M (ctrl + m) podczas zapisywania do pliku. Skończyło się na tym, że użyłem Perla z tymi samymi parametrami.
Steve Tauber

2
Należy pamiętać, że drugi kod wstawia specjalny kod nowej linii LF CR (odwrotność MS-DOS CR LF)! Zarówno systemy uniksopodobne, jak i Mac OS X używają tylko LF ( \n).
pabuk

Coś innego w mojej ekspresji seda powodowało tyle nieszczęścia (pomimo tego, że działało dobrze bez echo...i nowej linii), że zrobiłem to w vimie.
Ahmed Fasih

1
Lub po prostu: sed "s/regexp/`echo`/g"- spowoduje to powstanie pojedynczego LF zamiast LF-CR
mojuba

2
@mojuba: Nie: `echo`da w wyniku pusty ciąg , ponieważ podstawienia poleceń niezmiennie przycinają wszystkie końcowe znaki nowej linii. Nie ma sposobu, aby użyć podstawiania poleceń do bezpośredniego wstawienia pojedynczego \n\rznaku nowej linii (a wstawienie - tj. Dodatkowej litery CR - jest okropnym pomysłem).
mklement0

15
echo one,two,three | sed 's/,/\
/g'

1
+1 działało idealnie i całkiem prosto do przodu / łatwe do zapamiętania
gMale

2
Ta odpowiedź jest w rzeczywistości rozwiązaniem sed , a nie rozwiązaniem bash . Wszystko, co używa takich konstrukcji, $'\n'polega na powłoce w celu wygenerowania nowej linii. Takie rozwiązania mogą nie być przenośne. Ten jest. Oczywiście jest to również duplikat drugiego przykładu w odpowiedzi tgamblin z 2009 roku.
ghoti

10

W tym przypadku nie używam seda. Używam tr.

cat Somefile |tr ',' '\012' 

Spowoduje to pobranie przecinka i zastąpienie go znakiem powrotu karetki.


1
Zauważyłem, że to też działa: cat Somefile | tr ',' '\n'YMMV
LS

9

Możesz używać jednowierszowych perl, podobnie jak w sedzie, z zaletą pełnego wsparcia wyrażeń regularnych w perlu (które jest znacznie potężniejsze niż to, co dostajesz z sed). Występuje również bardzo mała różnorodność między platformami * nix - perl to generalnie perl. Możesz więc przestać martwić się o to, jak sprawić, by wersja seda w Twoim systemie robiła to, co chcesz.

W takim przypadku możesz to zrobić

perl -pe 's/(regex)/\n$1/'

-pe umieszcza perl w pętli "wykonaj i wydrukuj", podobnie jak normalny tryb działania seda.

' cytuje wszystko inne, aby powłoka nie przeszkadzała

() otoczenie wyrażenia regularnego jest operatorem grupowania. $1po prawej stronie zamiany wypisuje wszystko, co zostało dopasowane w tych pasmach.

Wreszcie \njest nowa linia.

Niezależnie od tego, czy używasz nawiasów jako operatora grupującego, musisz uciec przed wszystkimi nawiasami, które próbujesz dopasować. Zatem wyrażenie regularne pasujące do wzorca wymienionego powyżej byłoby czymś w rodzaju

\(\d\d\d\)\d\d\d-\d\d\d\d

\(lub \)dopasowuje dosłowny paren i \ddopasowuje cyfrę.

Lepszy:

\(\d{3}\)\d{3}-\d{4}

Wyobrażam sobie, że możesz dowiedzieć się, co robią liczby w nawiasach klamrowych.

Dodatkowo możesz użyć separatorów innych niż / dla swojego wyrażenia regularnego. Więc jeśli chcesz dopasować / nie musisz od tego uciekać. Każde z poniższych jest odpowiednikiem wyrażenia regularnego na początku mojej odpowiedzi. Teoretycznie można zastąpić standard (y) dowolnym znakiem .

perl -pe 's#(regex)#\n$1#'
perl -pe 's{(regex)}{\n$1}'

Kilka myśli końcowych.

używanie -nezamiast -pedziała podobnie, ale nie drukuje automatycznie na końcu. Może być przydatne, jeśli chcesz drukować samodzielnie. Na przykład, oto grep-alike ( m/foobar/to dopasowanie wyrażenia regularnego):

perl -ne 'if (m/foobar/) {print}'

Jeśli uważasz, że radzenie sobie z nowymi liniami jest kłopotliwe i chcesz, aby było to dla Ciebie magiczne, dodaj -l . Nie jest to przydatne dla OP, który pracował z nowymi liniami.

Dodatkowa wskazówka - jeśli masz zainstalowany pakiet pcre, jest on dołączony do zestawu pcregrep, który używa wyrażeń regularnych zgodnych z Perlem.


4

Hmm, po prostu znaki nowej linii ze znakami ucieczki wydają się działać w nowszych wersjach sed(mam GNU sed 4.2.1),

dev:~/pg/services/places> echo 'foobar' | sed -r 's/(bar)/\n\1/;'
foo
bar

1
Jak wspomniano, działa to z różnymi wersjami GNU sed, ale nie z sedem dołączonym do macOS.
LS

4
echo pattern | sed -E -e $'s/^(pattern)/\\\n\\1/'

działał dobrze na El Captitan ze ()wsparciem


To zadziałało świetnie, a nawet dajesz pełne polecenie testowania i ekstrapolacji, aby specjalizować się do własnych celów. Dobra robota!
jxramos

3

Aby wstawić znak nowej linii do strumienia wyjściowego w systemie Linux, użyłem:

sed -i "s/def/abc\\\ndef/" file1

Gdzie file1było:

def

Przed wymianą seda na miejscu oraz:

abc
def

Po wymianie seda na miejscu. Zwróć uwagę na użycie \\\n. Jeśli wzorce mają "wewnątrz, ucieknij za pomocą \".


U mnie powyższy kod nie działa. sedwstawia \nzamiast LF, ponieważ pobiera \\nparametr z powłoki. --- Ten kod działa: sed -i "s/def/abc\ndef/" file1. --- GNU sed version 4.2.1, GNU bash, version 4.1.2(1) / 4.2.25(1)(CentOS wersja 6.4 / Ubuntu 12.04.3).
pabuk

2

w sed możesz odwołać się do grup w swoim wzorcu za pomocą "\ 1", "\ 2", .... więc jeśli szukany wzorzec to "WZÓR" i chcesz wstawić przed nim "PRZED" , możesz użyć, sans ucieczki

sed 's/(PATTERN)/BEFORE\1/g'

to znaczy

  sed 's/\(PATTERN\)/BEFORE\1/g'

Właśnie zrobiłem: zawartość pliku testowego = "ABC ABC ABC". Uruchomiono plik testowy "sed 's / \ (ABC \) / \ n \ 1 / g', otrzymałem znaki nowej linii. Poeksperymentuj ze znakami ucieczki, spróbuj dodać jedną rzecz naraz do wzorca, np. Upewnij się, że pasujesz do wzorzec, a następnie sprawdź dopasowanie grupy, a następnie dodaj sprawdzanie nowej linii
Steve B.

Właśnie tego spróbowałem i otrzymałem „nABC nABC nABC”. Czy używasz innej wersji seda?
Todd Gamblin

ucieczka pocisku prawdopodobnie przeszkadza w próbach tgamblina. umieszczenie pełnych argumentów sed w pojedynczych cudzysłowach, tak jak zrobił to Steve B., powinno to naprawić. Możliwe jednak, że różne wersje seda nie rozumieją \ n znaku nowej linii.
Dan Pritts

2

Możesz to również zrobić za pomocą awk, używając -vdo podania wzorca:

awk -v patt="pattern" '$0 ~ patt {gsub(patt, "\n"patt)}1' file

Sprawdza, czy linia zawiera podany wzorzec. Jeśli tak, dodaje nowy wiersz na początek.

Zobacz podstawowy przykład:

$ cat file
hello
this is some pattern and we are going ahead
bye!
$ awk -v patt="pattern" '$0 ~ patt {gsub(patt, "\n"patt)}1' file
hello
this is some 
pattern and we are going ahead
bye!

Zauważ, że wpłynie to na wszystkie wzorce w linii:

$ cat file
this pattern is some pattern and we are going ahead
$ awk -v patt="pattern" '$0 ~ patt {gsub(patt, "\n"patt)}1' d
this 
pattern is some 
pattern and we are going ahead

1
co w tym robi 1?
whatahitson

1
@whatahitson 1jest używane w Awk jako skrót od {print $0}. Powodem jest to, że każdy warunek, który ma wartość True, wyzwala domyślne działanie Awk, które polega na wydrukowaniu bieżącego rekordu.
fedorqui 'SO przestań szkodzić'

1

To działa w MAC dla mnie

sed -i.bak -e 's/regex/xregex/g' input.txt sed -i.bak -e 's/qregex/\'$'\nregex/g' input.txt

Dono, czy jest idealny ...


1

Po przeczytaniu wszystkich odpowiedzi na to pytanie, nadal zajęło mi wiele prób, aby uzyskać poprawną składnię poniższego przykładowego skryptu:

#!/bin/bash
# script: add_domain
# using fixed values instead of command line parameters $1, $2
# to show typical variable values in this example
ipaddr="127.0.0.1"
domain="example.com"
# no need to escape $ipaddr and $domain values if we use separate quotes.
sudo sed -i '$a \\n'"$ipaddr www.$domain $domain" /etc/hosts

Skrypt dołącza nowy wiersz, \npo którym następuje kolejny wiersz tekstu, na końcu pliku za pomocą jednego sedpolecenia.


1
sed -e 's/regexp/\0\n/g'

\ 0 to null, więc twoje wyrażenie jest zastępowane przez null (nic), a następnie ...
\ n jest nową linią

Na niektórych odmianach Uniksa nie działa, ale myślę, że to rozwiązanie twojego problemu.

echo "Hello" | sed -e 's/Hello/\0\ntmow/g'
Hello
tmow

0

W vi w Red Hacie mogłem wstawić powrót karetki używając tylko znaku \ r. Uważam, że to wewnętrznie wykonuje „ex” zamiast „sed”, ale jest podobny, a vi może być innym sposobem na zbiorcze edycje, takie jak poprawki kodu. Na przykład. Otaczam wyszukiwane hasło instrukcją if, która nalega na powrót karetki po nawiasach klamrowych:

:.,$s/\(my_function(.*)\)/if(!skip_option){\r\t\1\r\t}/

Zwróć uwagę, że kazałem też wstawić kilka zakładek, aby lepiej się wyrównać.

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.