Skocz do zawartości
atsiv

Bash, Sed - Howto

Rekomendowane odpowiedzi

Wpadłem na taki pomysł, aby odwrócić tekst:

>sed '1!G;h;$!d' plik

Działa, ale zastanawiam się dlaczego na końcu tworzy mi się pusta linijka oraz gdy chce usunąć dwa pierwsze wiersze nie mogę coś:

>sed '1!G;h;$!d;1,2d;/^$/d' plik

Moim zdanie wydaje się być dobrze, no chyba, że coś źle załapałem.

 

Usuwa dwie ostatnie linijki:

>sed 'N;$!P;$!D;$d'

 

A jak dam:

>sed 'N;$!P;$!D;$d;3,4d'

Nie chce usunąć dodatkowo 3,4 linijkę.

 

Jak dla mnie staje się skompilowany ten sed :P

Edytowane przez atsiv

Udostępnij tę odpowiedź


Odnośnik do odpowiedzi
Udostępnij na innych stronach

Gość <account_deleted>

1. Stosujesz "d" oraz "$!D", które powoduje restart skryptu (od początku) - jak ma zadziałać reszta? Można wyczyścić pattern buffer (lub jego część) przy użyciu "s" -> patrz mój wcześniejszy przykład.

2. "N" oprócz dodania nowej linii do bufora powoduje również zwiększenie licznika linii -> '3d' może przeoczyć linię jeśli wcześniej pojawi się "d" lub "D".

 

...poza tym widać że zaczynasz dobrze kombinować ;)

Udostępnij tę odpowiedź


Odnośnik do odpowiedzi
Udostępnij na innych stronach

Masz na myśli, np. ten przykład i to s?

 

>cat /file | sed -n '{N;N;:nxtln;N;P;s_^[^\n]*\n__;b nxtln}'

 

Z ciekawości co oznaczają: :nxtln; s_; n__?

 

>sed 's/kot//'

usuwa mi tylko wyraz z wiersza, a nie cały wiersz... jeszcze muszę się pouczyć

Edytowane przez atsiv

Udostępnij tę odpowiedź


Odnośnik do odpowiedzi
Udostępnij na innych stronach

Gość <account_deleted>

Komenda "s" pozwala na definicję separatora - jeśli jako pierwsze po "s" pojawi się "_" to jest uznawane za separator dla dalszych działań, f.e. s_e_E_g = s/e/E/g = s|e|E|g. Głównym celem jest zwiększenie czytelności skryptu, np. w sytuacji: s/\n\t/\n/g -> s_\n\t_\n_g.

 

Jeśli chodzi o :nxtln - jest to definicja etykiety (label). Jest ona używana w pętli: ;b nxtln; = skok bezwarunkowy do linii z etykietą "nxtln"

Dzięki temu powstało FIFO na pattern buffer. Bufor trzyma 3 linie, pierwsza jest drukowana przez P, usuwana przez s, dodaję następną linię N i tak w kółko -> na końcu pliku linie pozostające w buforze są pominięte lub jak kto woli "wycięte" z pliku.

 

edit: właśnie się resetuję, co skutkuje w postaci literówek ;)

Edytowane przez tomazzi

Udostępnij tę odpowiedź


Odnośnik do odpowiedzi
Udostępnij na innych stronach

No dzięki :) teraz naprawdę zaczynam już łapać o co tu lotto ;)

 

Jeszcze zadałbym ostatnie pytanie, da się zrobić w sedzie, aby:

tekst1

tekst2

tekst3

był wyświetlany w jednym wierszu: teskt1 tekst2 tekst3?

 

Na tygodniu będę miał trochę czasu i wezmę się ostro za seda, bo już wszystko jaśniej widzę ;) jeszcze raz wielkie dzięki.

Udostępnij tę odpowiedź


Odnośnik do odpowiedzi
Udostępnij na innych stronach

Gość <account_deleted>

łee, to jest banał - powinieneś już to wiedzieć ;)

>echo -e "text1\ntext2\ntext3"

>text1

>text2

>text3

>echo -e "text1\ntext2\ntext3" | sed -n 'N;N;s_\n_ _g p'

>text1 text2 text3

 

interpretacja: sed dostaje pierwszą linię by_default, pobieramy jeszcze 2: N;N; i zamieniamy wszystkie newline na spacje "s_\n_ _g" ;)

 

edit: powinieneś chyba zmienić tytuł tematu np na: bash, sed - howto ;)

Edytowane przez tomazzi

Udostępnij tę odpowiedź


Odnośnik do odpowiedzi
Udostępnij na innych stronach

Czemu, już się zmienia tytuł tematu na bardziej odpowiedni ;)

 

Właśnie powoli uczę się tego seda i powoli zaczynam coraz więcej kumać ;) Mam nadzieję, że któregoś dnia TIR za to podrzuci Tobie kratę browarów bo jest za co dziękować ;)

Udostępnij tę odpowiedź


Odnośnik do odpowiedzi
Udostępnij na innych stronach

Gość <account_deleted>

Darmowe browary w każdej ilości są mile widziane (ofc, jeśli $ilość>0) :lol:

Edytowane przez tomazzi

Udostępnij tę odpowiedź


Odnośnik do odpowiedzi
Udostępnij na innych stronach

Wziąłem się za naukę seda i już łapię coraz więcej, ale są jeszcze rzeczy, których pojąć nie mogę...

Załóżmy, że mam plik, w którym znajdują się kilka linijek:

___________

mint ...

mint ...

ubuntu edgy

ubuntu feisty

...

...

ubuntu jaunty

___________

 

Chciałbym, aby sed pokazał wszystkie linijki tam gdzie występuje wyraz ubuntu oraz kasował cała linijkę ubuntu jaunty, ponieważ teraz mam uruchomiony ten system u siebie.

1 metoda jest dla mnie jasna:

^sed '/^ubuntu/!d;/'`lsb_release -cs`'/d'

 

Chciałem to samo osiągnąć tyle, że druga metodą:

^sed -n '/^ubuntu/p'

^sed -n '/'`lsb_release -cs`'/!p'

 

Gdy z dwóch poleceń zrobię jedno:

^sed -n '/^ubuntu/p;/'`lsb_release -cs`'/!p'

sed pokazuje wtedy pokazuje taki wynik:

___________

mint ...

mint ...

ubuntu edgy

ubuntu edgy

ubuntu feisty

ubuntu feisty

...

...

ubuntu jaunty

___________

 

Co źle robię, ponieważ z założenia mój końcowy wynik tak powinien wyglądać:

___________

ubuntu edgy

ubuntu feisty

...

...

___________

Udostępnij tę odpowiedź


Odnośnik do odpowiedzi
Udostępnij na innych stronach

Gość <account_deleted>

sed '/^ubuntu/!d;/'`lsb_release -cs`'/d'

sed -n '/^ubuntu/p;/'`lsb_release -cs`'/!p'

Wyjaśnienie jest proste: "d" lub "D" powoduje restart skryptu (napisałem to już wcześniej), natomiast "p" - nie.

 

pierwsza wersja działa tak:

a) jeśli nie ma "ubuntu", to usuń i pobierz następną linię <restart przez d>

b) jeśli jest "ubuntu" nie usuwaj (d nie trafia we wzorzec) , ale jeśli jest jeszcze "jaunty", to jednak usuń i pobierz następną linię

 

druga wersja działa tak:

a) znajdź "ubuntu" i wydrukuj ; jak nie ma "jaunty" to drukuj - no i masz powtórzenia linii

b) znajdź "ubuntu" i wydrukuj ; jak jest "jaunty" to nie drukuj - no i co z tego że "nie drukuj", skoro pierwsza komenda już to zrobiła ;)

Edytowane przez tomazzi

Udostępnij tę odpowiedź


Odnośnik do odpowiedzi
Udostępnij na innych stronach

druga wersja działa tak:

a) znajdź "ubuntu" i wydrukuj ; jak nie ma "jaunty" to drukuj - no i masz powtórzenia linii

b) znajdź "ubuntu" i wydrukuj ; jak jest "jaunty" to nie drukuj - no i co z tego że "nie drukuj", skoro pierwsza komenda już to zrobiła ;)

Czyli tutaj coś źle robię i nie bardzo wiem jak powinno być dobrze ;)

Udostępnij tę odpowiedź


Odnośnik do odpowiedzi
Udostępnij na innych stronach

Gość <account_deleted>

stary, minął prawie miesiąc od poprzedniego posta - masz odpowiedź dlaczego nie działa wersja 2 i jak działa wersja 1 -> to wystarczająca dawka informacji.

Udostępnij tę odpowiedź


Odnośnik do odpowiedzi
Udostępnij na innych stronach

Mam takie pytanie. Bawię się sedem i zrobiłem takie coś, że z pliku sed wyciął 5 linijek i chciałbym, żeby wyciął jeszcze 1 linijkę, ale nie z pliku tylko z tych 5, da się tak?

Edytowane przez atsiv

Udostępnij tę odpowiedź


Odnośnik do odpowiedzi
Udostępnij na innych stronach

Mam takie pytanie. Bawię się sedem i zrobiłem takie coś, że z pliku sed wyciął 5 linijek i chciałbym, żeby wyciął jeszcze 1 linijkę, ale nie z pliku tylko z tych 5, da się tak?

Udostępnij tę odpowiedź


Odnośnik do odpowiedzi
Udostępnij na innych stronach

Czy mógłbym prosić o jeszcze jedną pomoc w sprawie SEDa:

s/(^|[^0-9.])([0-9]+)([0-9]{3})/\1\2,\3/g

 

Chodzi mi o: \1\2,\3/

 

Jeśli mam długą linijkę i dam \10\16\88 to SED jest uparty i zamiast 10 1 traktuje jako pozycję, a 0 jako nowy znak, który mam dodać, dlaczego tak robi?

Udostępnij tę odpowiedź


Odnośnik do odpowiedzi
Udostępnij na innych stronach

Gość <account_deleted>

Jeśli mam długą linijkę i dam \10\16\88 to SED jest uparty i zamiast 10 1 traktuje jako pozycję, a 0 jako nowy znak, który mam dodać, dlaczego tak robi?

Odpowiedziałem na Twoje PM, ale co by inni też mogli z tego skorzystać dopiszę:

sed pozwala na stosowanie do 9 frgmentów wzorca ( \1 do \9 ) w takim wydaniu jak wyżej (jedna grupa komend).

W 99.99% przypadków jest to aż za dużo. Ograniczenie to wynika z POSIX / RegEx -> "\" znak "escape sequence" definiuje pojedyńczy znak traktowany "dosłownie" (literal)

Rozwiązania problemu są 2:

- przemyśleć jeszcze raz sposób zapisu danych -> polecam...

- zapoznać się z możliwościami oferowanymi przez "Hold Buffer" - a są one praktycznie nieskończone ;)

 

Przykład użycia Hold Buffer znajdziesz np. w moim temacie o LIRC, w kodzie skryptu (konwersja sekund do nanosekund) -> jest to "zapasowy" Pattern Buffer, dzięki któremu można przetwarzać linię etapami.

Udostępnij tę odpowiedź


Odnośnik do odpowiedzi
Udostępnij na innych stronach

Ogólnie wielkie dzięki za wytłumaczenie, ponieważ nie mogłem nic na ten temat w sieci znaleźć.

 

 

Jeszcze jedno mam do Ciebie teoretyczne pytanie. Załóżmy sobie że mam plik, w którym sed wycina powiedzmy 10 linijek i chciałbym aby on z tych dziesięciu wyciął jeszcze 5, tzn. wiem że mogę zrobić takie coś:

 

>sed "blablabla" plik | sed "blablabla" > plik2

 

a czy w ten sposób się da:

 

>sed "najpierw wytnie 10 linijek, potem z tych dziesięciu jeszcze 5" plik

 

?

 

Tak na marginesie widzę, że posiadasz ogromna wiedzę o bashu itd., czy są jakieś dobre kursy, które warto wg Ciebie przeczytać? Bo ja to mam takie typowo bardziej oparte na przykładach i z takich nie wiele można się dowiedzieć (nie dowiem się dlaczego musi być tak, a nie inaczej oraz dlaczego tak itd.), wolałbym coś w stylu podręcznik w którym można znaleźć ogólne teorie, bo bez tego jest trudniej i zanim człowiek do czegoś dojdzie to minie.

Udostępnij tę odpowiedź


Odnośnik do odpowiedzi
Udostępnij na innych stronach

Gość <account_deleted>

tzn: chcesz chyba żeby sed zostawił 10 linijek, z których następnie wytnie 5? -> tak oczywiście, jest to możliwe - z tym że przy małej ilości linii lepiej zrobić to w jednym skrypcie seda - np. poprzez "zmagazynowanie" linii w którymś z buforów, natomiast przy dużej liczbie (powiedzmy kilka tys. + ) lepiej odpalić 2x sed - opóźnienia cache systemu stają się mało istotne w obec możliwości równoległego przetwarzania strumieni.

 

...10 stron tekstu można zastąpić 1-nym przykładem (+ ew. opis) -> na necie jest masa tutoriali dla Basha - być może problemem jest to że najlepsze są w przygniatającej większości po angielsku...

Udostępnij tę odpowiedź


Odnośnik do odpowiedzi
Udostępnij na innych stronach

Gość <account_deleted>

Zostaje przecierz \n <newline>

Dlaczego nie możesz poprostu użyć "d"?

Edytowane przez tomazzi

Udostępnij tę odpowiedź


Odnośnik do odpowiedzi
Udostępnij na innych stronach

Ok, zrobiłem tak jak napisałeś i jest dobrze.

 

Hmm... właśnie wróciłem z zagranicy i mam trochę czasu pobawić się sedem. Nie rozumiem jak mam "zmagazynować" linijkę, w którymś z buforów, mógłbyś to bardziej przybliżyć?

 

Mam jeszcze jeden mały problemik, powiedzmy, że jest taki ciąg znaków w pliku o nazwie "file":

abcdefghijklm
i daje takie coś:

sed "s/ab/bc/;s/bc/cd/;<itd.>" file

Weźmy pod uwagę, że tak na prawdę nie wiem jakie są litery w pliku i nie muszą być one alfabetycznie, mogą być mieszane. Myślałem, że w ten sposób w pliku będą zmieniane litery co dwie, ale tak nie jest, ponieważ najpierw zmienia się 1 i 2, dalej 2 i 3... a chciałem uzyskać efekt zmień 1 i 2 literę na xx, dalej 3 i 4 na yy itd. Oczywiście wpadłem na pomysł, aby co dwie litery stawiać kropki i wtedy uzyskuję swój efekt (na samym końcu usuwam te kropki sedem), ale nie jest to eleganckie rozwiązanie i teraz pytanie czy da się inaczej?

Edytowane przez atsiv

Udostępnij tę odpowiedź


Odnośnik do odpowiedzi
Udostępnij na innych stronach

Gość <account_deleted>

>echo "można inaczej" | sed -n 's_\(.\)\(.\)_\2\1_gp'

omnż anicaezj

>echo "omnż anicaezj" | sed -n 's_\(.\)\(.\)_\2\1_gp'

można inaczej

 

>echo "pary znaków" | sed -n 's_\(..\)\(..\)_\2\1_gp'

rypana zków ( :lol: )

>echo "rypana zków" | sed -n 's_\(..\)\(..\)_\2\1_gp'

pary znaków

 

\(\) - definiuje fragment wzorca. Cały ciąg (pasujący do wszystkich fragmentów) jest referowany klasycznie przez '&' a fragmenty ciągu pasujące do fragmentów wzorca przez numery \1...\9. Użycie 'g' powoduje rekursywne przetwarzanie, czyli to co chciałeś ;)

Edytowane przez tomazzi

Udostępnij tę odpowiedź


Odnośnik do odpowiedzi
Udostępnij na innych stronach

To to znam, nawet akurat dobrze rozumiem i miałem raczej co innego na myśli. No cóż chyba nie najlepiej wytłumaczyłem :P Jeszcze raz zacznę. Załóżmy sobie, że jest taki ciąg znaków:

1234567890123456789012907856... (oczywiście ilość i kombinacja nie jest znana)

 

i teraz chcę, aby co dwa znaki zmieniać na 3:

>echo 1234567890 | sed 's/12/123/g ; s/34/345/g ; ... itd.'

 

czyli ma to tak wyglądać mniej więcej:

1234567890... na 123345567...

 

ale ten mój sposób nie działa tak jakbym chciał. Jeśli 12 zmieni na 123, to dalej z tego 123 dwie pierwsze też się zmienią, a chciałbym żeby tego już nie robił. Wiem, że przyczyną tego jest opcja "g", ale bez niej dalszy ciąg znaków się nie zmieni. Sam siebie kopię, bo tym sposobem zmieniam dwa znaki niezmienione i dwa z tych trzech jeśli będą zgadzać się itd.

Na razie znalazłem taki sposób:

1. sed co drugi znak stawia kropkę, czyli 12.34.56.78.90...

2. sed zmienia znaki w sposób:

>echo .12..34..56..78..90. | sed 's/.12./.123./g ; s/.34./.345./g ; ... itd.' (wtedy: .123..345..567...)

3. Usuwam te kropki, które wlepiłem wcześniej i ten efekt mój osiągnąłem (123345567...123345567...)

Teraz pozostaje pytanie, czy da się to zrobić jakoś prościej? ;) Czy właśnie w ten sposób muszę kombinować w takich sytuacjach?

 

Aha, wcześniej pisałeś o "magazynowaniu linijek", czy mógłbyś teoretycznie przybliżyć jak się to robi. Na razie robię tak:

>echo abcdfg... | sed 'wzór 1' | sed 'wzór 2'

Ale wydaje mi się, że chyba źle robię to.

Edytowane przez atsiv

Udostępnij tę odpowiedź


Odnośnik do odpowiedzi
Udostępnij na innych stronach

Gość <account_deleted>

czyli ma to tak wyglądać mniej więcej:

1234567890... na 123345567...

Ależ nikt nie broni stosować powtórzeń przy zastępowaniu pasujących fragmentów ciągu:

echo "1234567890" | sed -n 's_\(.\)\(.\)_\1\2\2_gp'

122344566788900

 

Analogicznie można wrzucić '&&' itd.

Aby uzyskać zmianę tylko co 3-go trafienia wystarczy w pierwszym sub-patternie kazać znaleźć 2 dowolne znaki zamiast jednego:

echo "1234567890" | sed -n 's_\(..\)\(.\)_\1\2\2_gp'

1233456678990...

 

Poza tym nadal można stosować numery trafień w połączeniu z 'g', czyli od którego trafienia sed ma działać rekursywnie:

echo "1234567890" | sed -n 's_\(..\)\(.\)_\1\2\2_2gp'

123456678990

 

Hold buffer jest jak widać niepotrzebny w tym zadaniu ;)

Nie bardzo widzę sens przepisywania źródeł z neta, dlatego ponownie polecam Ci zapoznać się z tym tutorialem ponieważ jest to jeden z najlepszych. W razie kłopotów, niejasne kwestie pomogę rozwiązać tutaj ;)

Udostępnij tę odpowiedź


Odnośnik do odpowiedzi
Udostępnij na innych stronach

Przyznaję ci rację ;)

 

Dlaczego ten sed uparcie mi zmienia nawet już zmienione znaki:

>echo 'abcAbCcbA' | sed '{s/a/A/g;s/b/B/g;s/c/C/g};{s/A/a/g;s/B/b/g;s/C/c/g}'

>abcabccba

 

A ja chcę taki wynik:

>ABCaBcCBa

 

Hold buffer jest jak widać niepotrzebny w tym zadaniu

Czyli np:

>echo -e '1\n2\n3\n4\n5\n6\n7' | sed -n '1!G;h;$p'

 

Dobrze myślę?

Udostępnij tę odpowiedź


Odnośnik do odpowiedzi
Udostępnij na innych stronach

Gość <account_deleted>

W pierwszym przykładzie nie pobierasz żadnej nowej linii, więc kolejne filtry operują na tej samej zawartości bufora czyli modyfikują to co poprzednnie 's' zmieniło. Można to rozwiązać przez użycie skoków warunkowych 't', czyli omijać niektóre 's' gdy poprzednie coś znalazły. Można też użyć Hold Buffer do przechowywania oryginału/fragmentów już zmienionych.

 

Ciekawą zabawkę zrobiłeś z HB ;)

Udostępnij tę odpowiedź


Odnośnik do odpowiedzi
Udostępnij na innych stronach

>echo 'abcAbCcbA' | sed ':tutaj;N;s/a/A/g;s/b/B/g;s/c/C/g;t tutaj;s/A/a/g;s/B/b/g;s/C/c/g'

 

Z tym pokombinuję jutro, może coś z tego mi wyjdzie bo jak na razie nie działa mi.

 

Ciekawą zabawkę zrobiłeś z HB

Bo ten przykład jest krótki i jestem wstanie go zrozumieć, gorzej jak coś długiego będzie bo jeszcze nie do końca rozumiem kiedy i w jakiej kolejności mam dać h H G N P D itd. Właśnie tutaj mam w sumie największy problem jeśli chodzi o seda. Te inne rzeczy są dla mnie łatwiejsze do opanowania.

 

Tak na marginesie mógłbyś wyjaśnić jak wyciąć pierwszą linijkę i wkleić na sam koniec? Myślę, że będzie to dobre rozeznanie na początek bo do połowy to dojdę ale gorzej z drugą.

 

Na razie kombinuję tak:

>echo -e '12\n34\n56\n78' | sed -n '1P;h....'

Edytowane przez atsiv

Udostępnij tę odpowiedź


Odnośnik do odpowiedzi
Udostępnij na innych stronach

Gość <account_deleted>

Tak na marginesie mógłbyś wyjaśnić jak wyciąć pierwszą linijkę i wkleić na sam koniec? Myślę, że będzie to dobre rozeznanie na początek bo do połowy to dojdę ale gorzej z drugą.

 

Na razie kombinuję tak:

>echo -e '12\n34\n56\n78' | sed -n '1P;h....'

 

Bardzo łatwe:

>echo -e '12\n34\n56\n78' | sed -n '1 h;$ G;2,$ p;'

34

56

78

12

 

Czyli:

- linia 1: '1 h' kopiuj PB do HB (może być x, bez znaczenia w tym przypadku, chodzi o zapamiętanie bufora), nie drukujemy nic, bo 'p' jest wykonywane dla innego zakresu num. linii.

- linie 2 do ostatniej: '2,$ p' zawsze drukuj PB,

z tym że wcześniej sprawdzamy czy aktualnie pobrana linia jest ostatnia: '$ G' i w takim przypadku dodajemy HB do PB przed wydrukowaniem

;)

Edytowane przez tomazzi

Udostępnij tę odpowiedź


Odnośnik do odpowiedzi
Udostępnij na innych stronach

Dołącz do dyskusji

Możesz dodać zawartość już teraz a zarejestrować się później. Jeśli posiadasz już konto, zaloguj się aby dodać zawartość za jego pomocą.

Gość
Dodaj odpowiedź do tematu...

×   Wklejono zawartość z formatowaniem.   Przywróć formatowanie

  Dozwolonych jest tylko 75 emoji.

×   Odnośnik został automatycznie osadzony.   Przywróć wyświetlanie jako odnośnik

×   Przywrócono poprzednią zawartość.   Wyczyść edytor

×   Nie możesz bezpośrednio wkleić grafiki. Dodaj lub załącz grafiki z adresu URL.

Ładowanie



×
×
  • Dodaj nową pozycję...