Jak wykryć koniec linii za pomocą sed

15

Szukam sposobu na wykonanie zamiany tylko wtedy, gdy ostatni znak jest znakiem nowej linii sed.

Na przykład:

lettersAtEndOfLine

zostaje zastąpiony, ale nie jest to:

lettersWithCharacterAfter&

Ponieważ sednie działa dobrze z nowymi liniami, nie jest tak prosty jak

$ sed -E "s/[a-zA-Z]*\n/replace/" file.txt

Jak można tego dokonać?

Matthew D. Scholefield
źródło

Odpowiedzi:

21

Ze standardową sed, będzie nigdy zobaczyć nowego wiersza w tekście odczytać z pliku. Jest tak, ponieważ sedczyta wiersz po wierszu, dlatego nie ma nowej linii na końcu tekstu bieżącej linii w obszarze sedwzorów. Innymi słowy, sedczyta dane rozdzielane znakiem nowej linii, a separatory nie są częścią tego, co sedwidzi skrypt.

Wyrażenia regularne można zakotwiczać na końcu wiersza za pomocą $(lub na początku za pomocą ^). Zakotwiczenie wyrażenia na początku / końcu linii wymusza dopasowanie dokładnie tam, a nie tylko w dowolnym miejscu linii.

Jeśli chcesz zamienić coś pasujące do wzoru [A-Za-z]*na końcu linii na coś, a następnie zakotwicz wzór w następujący sposób:

[A-Za-z]*$

... wymusi dopasowanie na końcu linii i nigdzie indziej.

Ponieważ jednak [A-Za-z]*$również nic nie pasuje (na przykład pusty ciąg obecny na końcu każdej linii), musisz wymusić dopasowanie czegoś , np. Poprzez określenie

[A-Za-z][A-Za-z]*$

lub

[A-Za-z]\{1,\}$

Zatem twoja linia poleceń sed będzie w ten sposób

$ sed 's/[A-Za-z]\{1,\}$/replace/' file.txt

Nie użyłem -Etutaj przełącznika, ponieważ nie jest potrzebny. Dzięki temu mógłbyś napisać

$ sed -E 's/[A-Za-z]+$/replace/' file.txt

To kwestia gustu.

Kusalananda
źródło
Komentarze nie są przeznaczone do rozszerzonej dyskusji; ta rozmowa została przeniesiona do czatu .
Kusalananda
3
sed "s/[a-zA-Z]*$/replace/" input.txt > result.txt

Lub długi złożony niepotrzebny sposób:

Dowiedziałem się, że można to zrobić, nadal używając sed, za pomocą tr. Możesz przypisać inny znak reprezentujący koniec linii. Należy użyć innej postaci tymczasowej, w tym przypadku „”. Użyjmy „~” do przedstawienia końca linii:

tr '\n' '`' <input.txt >output.txt
sed -i "s/`/~`/" output.txt
tr '`' '\n' <output.txt >result.txt

A następnie, aby wykonać rzeczywiste wyszukiwanie i zamianę, użyj „~” zamiast „\ n”:

sed -i -E "s/[a-zA-Z]*~/replace/" result.txt

A potem posprzątaj dodatkową postać w innych liniach:

sed -i "s/~//" result.txt

Oczywiście można to wszystko połączyć ze sobą, tworząc coś w rodzaju:

tr '\n' '`' <input.txt | sed -e "s/`/~`/" | tr '`' '\n' | sed -E -e "s/[a-zA-Z]*~/replace/" | sed "s/~//" > result.txt
Matthew D. Scholefield
źródło
3
Nie jestem pewien, czy rozumiem ... Dlaczego po prostu nie zakotwiczysz do końca linii $? np.s/[a-zA-Z]*$/replace/
don_crissti
1
2 punkty: 1) Lepiej użyj \+zamiast, *ponieważ ta ostatnia dopuszcza zero liter na końcu łańcucha; 2) Możesz użyć klasy postaci [[:alpha:]]. Więc:sed 's/[[:alpha:]]\+$/replace/' file
glenn jackman
@glennjackman Po co jest ukośnik odwrotny przed plusem? Czy to nie pasuje do postaci dodatkowej?
Matthew D. Scholefield
1
GNU sed bez -ropcji używa tej składni wyrażeń regularnych .
glenn jackman
0

Z (złamanego) fragmentu kodu, który opublikowałeś, wydaje się, że chcesz również zastąpić nowy wiersz. W takim przypadku samo zakotwiczenie wyrażenia regularnego nie może ci pomóc. Oto rozwiązanie:

sed '/[[:alpha:]]\+$/{N;s/[[:alpha:]]\+\n/replace/}' your_file

Zepsuty:

  • /[a-zA-Z]\+$/{} oznacza zastosowanie wszystkiego, co wchodzi w curlies, do linii pasujących do wyrażenia regularnego.
  • Wyrażenie regularne korzysta z zakotwiczenia widocznego we własnej odpowiedzi , zmodyfikowanego w celu uwzględnienia komentarzy glenn jackman .
  • Wewnątrz curlies Noznacza „dołącz następną linię do aktywnego bufora” (co sednazywa „przestrzenią wzorów”)
  • Wreszcie s///oświadczenie jest wymaganą substytucją. Teraz działa, ponieważ przestrzeń wzorów zawiera dwie kolejne linie, a zatem nowa linia jest jej częścią.
Joseph R.
źródło
0

Aby znaleźć koniec linii, użyj znaku $ :

Bez kotwicy na końcu linii:

sed -n '/pattern/p' file 

Bez kotwicy na końcu linii:

sed -n '/pattern$/p' file
nieznany użytkownik
źródło