Co to takiego ten sed?
sed (ang. Stream EDitor – edytor strumieniowy) – program służący do przetwarzania plików tekstowych. Napisany został pierwotnie przez Lee E. McMahona dla systemu UNIX w roku 1973, w chwili obecnej dostępny jest praktycznie dla każdego systemu operacyjnego, dysponującego wierszem poleceń.
sed jest filtrem – pobiera dane w postaci wierszy tekstu ze standardowego wejścia, przetwarza je zgodnie z poleceniami podanymi w wierszu poleceń lub zapisanymi w pliku, a wyniki wysyła na standardowe wyjście.
Cechą charakterystyczną edytora sed jest to, że wykonuje on na danym wierszu wszystkie polecenia, jakie są do wykonania. Ponieważ przetwarzanie odbywa się wiersz po wierszu, sed nie ma praktycznie żadnych ograniczeń na wielkość przetwarzanego pliku. sed obsługuje wyrażania regularne (regexp), co sprawia, że jest naprawdę potężnym narzędziem.
Do czego mogę użyć sed?
sed może mieć bardzo wiele zastosowań. Poniżej wymienię kilka.
Kasowanie wybranych linii
Pisząc serię artykułów umieściłem w kodzie html komentarze. Komentarz w html rozpoczyna się od <!-- i kończy -->. Przykład:
sed (ang. Stream EDitor – edytor strumieniowy) – program służący do przetwarzania plików tekstowych. Napisany został pierwotnie przez Lee E. McMahona dla systemu UNIX w roku 1973, w chwili obecnej dostępny jest praktycznie dla każdego systemu operacyjnego, dysponującego wierszem poleceń. sed jest filtrem – pobiera dane w postaci wierszy tekstu ze standardowego wejścia, przetwarza je zgodnie z poleceniami podanymi w wierszu poleceń lub zapisanymi w pliku, a wyniki wysyła na standardowe wyjście. <!-- Tu przydałby się jakiś odpowiedni obrazek dla ilustracji działania sed. Sed bierze dane z wejście i wypisuje na wyjście. --> Cechą charakterystyczną seda jest to, że wykonuje on na danym wierszu wszystkie polecenia, jakie są do wykonania. Ponieważ przetwarzanie odbywa się wiersz po wierszu, sed nie ma praktycznie żadnych ograniczeń na wielkość przetwarzanego pliku. sed obsługuje wyrażania regularne (regexp), co sprawia, że jest naprawdę potężnym narzędziem.
Umieszczałem komentarze w ten sposób, że znaczniki <!-- oraz --> znajdują się na początku linii i poza nimi nie ma takiej w takiej linii innego tekstu. Chcę teraz usunąć komentarze z każdego artykułu, Jak to zrobić? Można oczywiście edytować każdy artykuł osobno. Jeśli artykułów jest kilka zajmie to chwilę ale jeśli jest ich 100? Wtedy z pomocą przychodzi sed. Dzięki temu, że znam format pliku mogę "zlecić" aby sed skasował:
- każdą linię, która zawiera jedynie
<!-- - każdą linię, która zawiera jedynie
--> - każdą linię pomiędzy 1. i 2.
Polecenie, które wykona to zadanie jest bardzo proste:
sed -e '/^<!--$/,/^-->$/d' sed.html > tmp.html
Kubek sed to najpotrzebniejsze komendy edytora sed.
Dzięki niemu, spis najważniejszych komend edytora
sed jest zawsze pod ręką, a właściwie na biurku. Ty też możesz go mieć.
Parametr -e mówi, że po nim następuje skrypt z komendami. Znaki ' zabezpieczają mój mini skrypt przed interpretacją przez powłokę systemu operacyjnego. W mini skrypcie podane jest polecenie usuwania linii d (polecenie sed są jednoliterowe). Jest również podany zakres linii - /^<!--$/,/^-->$/, dla których polecenie ma być wykonane. sed ma rozpocząć kasowanie linii, gdy napotka linię postaci ^<!--$ i zakończyć gdy napotka linię postaci ^-->$. Znaki ^ oraz $ są specjalnymi znakami wyrażeń regularnych i oznaczają odpowiednio początek i koniec linii. Wynik działania powyższego polecenia jest kierowany do pliku tmp.html. Korzystając z diff mogę sprawdzić, czy usunąłem te i jedynie te linie, które chciałem. Jeśli tak jest mogę zastąpić plik sed.html przez tmp.html.
Zamiana tekstu
W pewnym tekście piszę o właściwościach systemu UNIX. Jednak okazuje się, że opisywana przeze mnie cecha jest właściwa zarówno dla systemów operacyjnych UNIX jak i Linux. Zatem trzeba zamienić wyrażenie systemy operacyjne UNIX
na wyrażenie systemy operacyjne UNIX i Linux
. Mogę tego dokonać za pomocą komendy sed s///. Komenda s/str/rpl/, to polecenie wyszukania i zamiany jednego łańcucha znaków (str) na drugi (rpl). Łańcuchy są ograniczone poprzez znak /. Domyślnie komenda s zamienia jedynie pierwsze napotkane w linii wyrażenie. Jeśli dodam do komendy s flagę g, wtedy zostanie zamienione każde napotkane w linii wyrażenie. W naszym przykładzie str to systemy operacyjne UNIX
, zaś rpl to: systemy operacyjne UNIX i Linux
.
Aby zrealizować powyżej opisane zadanie należy wydać sed polecenie:
sed -e 's/systemy operacyjne UNIX/systemy operacyjne UNIX i Linux/g' plik_do_zmiany.txt > nowy_plik.txt
Wynik działania zostanie skierowany do pliku nowy_plik.txt.
Powyższe rozwiązanie jest jedynie przykładem na wykorzystanie polecenia s, działa poprawnie w przypadku gdy wyrażenie systemy operacyjne UNIX
jest zapisane w jednej linii.
Zaawansowany grep
Chciałbym wyszukać wszystkie linie, które są w komentarzu html oraz w których występuje słowo "sed". Oczywiście mogę w tym celu wykorzystać polecenie grep:
grep sed sed.html
, a następnie mozolnie w szukać odpowiedniej linii. Dzięki sed mogę wykonać powyższe zadanie znacznie łatwiej. Zadanie wykona polecenie:
sed -n -e '/^<!--$/,/^-->$/ s/\(.*\) sed \(.*\)/\1 sed \2/p' sed.html
Ponownie rozpatrujemy jedynie linie komentarza, czyli:
- każdą linię, która zawiera jedynie
<!-- - każdą linię, która zawiera jedynie
--> - każdą linię pomiędzy 1. i 2.
Za pomocą wyrażeń regularnych możemy opisać linię, którą szukamy jako:
.* sed .*
czyli dowolny ciąg znaków, spację słowo sed, spację oraz ponownie dowolny ciągu znaków.
Korzystając z możliwości sed pierwsze i drugie wystąpienie .* zamykamy pomiędzy \( oraz \). Otrzymujemy:
\(.*\) sed \(.*\)
Następnie ponownie wykorzystując możliwości sed do pierwszego wyrażenia \(.*\) możemy się odwołać poprzez \1, do drugiego \2. Teraz skorzystamy z komendy s///. W tym przypadku łańcuch \(.*\) sed \(.*\) zamienimy na \1 sed \2
Polecenia przyjmuje postać:
s/\(.*\) sed \(.*\)/\1 sed \2/p
Do komendy s dodaliśmy flagę p, która powoduje wyświetlenie na standardowe wyjście wyniku działania polecenia s.
Wywołanie sed uzupełniamy o parametr -n, dzięki której wyświetlone zostaną jedynie te linie, dla których zastosowane zostało polecenie s.
Całość, tak skonstruowanego polecenia stosujemy do pliku sed.html. Ostatecznie polecenie przyjmuje postać:
sed -n -e '/^<!--$/,/^-->$/ s/\(.*\) sed \(.*\)/\1 sed \2/p' sed.html
Pisanie gier
W sed można również napisać grę! Poniżej przykład gry w kółko i krzyżyk napisanej w sed. Aby zagrać należy zapisać poniższy kod w pliku (np.: gra.sh) i uruchomić: sed -f gra.sh.
1 {
s/.*/\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
1> | | \
-+-+-\
2> | | \
-+-+-\
3> | | \
\
^ ^ ^\
A B C\
/
h
a \
Enter the name of the square in which\
you would like to place a counter, or\
press [RETURN] if you would like me to\
begin.
n
}
/^$/ {
2 g
2 b mefirst
}
/[Aa]1/ {
g
s/1> |/1> X|/
t moved
b full
}
/[Aa]2/ {
g
s/2> |/2> X|/
t moved
b full
}
/[Aa]3/ {
g
s/3> |/3> X|/
t moved
b full
}
/[Bb]1/ {
g
s/1> \(.\)| |/1> \1|X|/
t moved
b full
}
/[Bb]2/ {
g
s/2> \(.\)| |/2> \1|X|/
t moved
b full
}
/[Bb]3/ {
g
s/3> \(.\)| |/3> \1|X|/
t moved
b full
}
/[Cc]1/ {
g
s/1> \(.|.\)| /1> \1|X/
t moved
b full
}
/[Cc]2/ {
g
s/2> \(.|.\)| /2> \1|X/
t moved
b full
}
/[Cc]3/ {
g
s/3> \(.|.\)| /3> \1|X/
t moved
b full
}
a \
I don't know which square you mean.\
Please enter your choice in the form 'C3'.
b
: full
a \
That particular square is already occupied.\
Please choose another!
b
: moved
/X|X|X/ b win
/ X.* X.* X/ b win
/|X|.*|X|.*|X|/ b win
/|.|X.*|.|X.*|.|X/ b win
/> X.*> .|X.*> .|.|X/ b win
/> .|.|X.*> .|X.*> X/ b win
/[OX]|[OX]|[OX].*[OX]|[OX]|[OX].*[OX]|[OX]|[OX]/ b draw
h
: wait
n
/^$/ !{
s/.*/Oh no you don't, it's my turn!!/
b wait
}
g
/O|O| / {
s/O|O| /O|O|O/
b loose
}
/ |O|O/ {
s/ |O|O/O|O|O/
b loose
}
/O| |O/ {
s/O| |O/O|O|O/
b loose
}
/> O.*> O.*> / b playA
/> O.*> .*> O/ b playA
/> .*> O.*> */ b playA
/|O|.*|O|.*| |/ b playB
/|O|.*| |.*|O|/ b playB
/| |.*|O|.*|O|/ b playB
/|.| .*|.|O.*|.|O/ b playC
/|.|O.*|.| .*|.|O/ b playC
/|.|O.*|.|O.*|.| / b playC
/> |.*|O|.*|.|O/ {
s/1> /1> O/
b loose
}
/> O|.*| |.*|.|O/ {
s/\(2> .|\) /\1O/
b loose
}
/> O|.*|O|.*|.| / {
s/\(3> .|.|\) /\1O/
b loose
}
/|.|O.*|O|.* |.|/ {
s/3> /3> O/
b loose
}
/|.|O.*| |.*O|.|/ {
s/\(2> .|\) /\1O/
b loose
}
/|.| .*|O|.*O|.|/ {
s/\(1> .|.|\) /\1O/
b loose
}
/X|X| / {
s/X|X| /X|X|O/
b finished
}
/ |X|X/ {
s/ |X|X/O|X|X/
b finished
}
/X| |X/ {
s/X| |X/X|O|X/
b finished
}
/> X.*> X.*> / b playA
/> X.*> .*> X/ b playA
/> .*> X.*> */ b playA
/|X|.*|X|.*| |/ b playB
/|X|.*| |.*|X|/ b playB
/| |.*|X|.*|X|/ b playB
/|.| .*|.|X.*|.|X/ b playC
/|.|X.*|.| .*|.|X/ b playC
/|.|X.*|.|X.*|.| / b playC
/> |.*|X|.*|.|X/ {
s/1> /1> O/
b finished
}
/> X|.*| |.*|.|X/ {
s/\(2> .|\) /\1O/
b finished
}
/> X|.*|X|.*|.| / {
s/\(3> .|.|\) /\1O/
b finished
}
/|.|X.*|X|.* |.|/ {
s/3> /3> O/
b finished
}
/|.|X.*| |.*X|.|/ {
s/\(2> .|\) /\1O/
b finished
}
/|.| .*|X|.*X|.|/ {
s/\(1> .|.|\) /\1O/
b finished
}
: mefirst
/2> .| |/ {
s/2> \(.\)| |/2> \1|O|/
2 !a \
Don't mind if I do!
b finished
}
/[13]> |/ {
s/\([13]> \) |/\1O|/
b finished
}
/[13]> .|.| / {
s/\([13]> .|.|\) /\1O/
b finished
}
/| |/ {
s/| |/|O|/
b finished
}
/2> / {
s/2> /2> O/
b finished
}
s/\(2> .|.|\) /\1O/
: finished
/[OX]|[OX]|[OX].*[OX]|[OX]|[OX].*[OX]|[OX]|[OX]/ b draw
/O|O|O/ b loose
/ O.* O.* O/ b loose
/|O|.*|O|.*|O|/ b loose
/|.|O.*|.|O.*|.|O/ b loose
/> O.*> .|O.*> .|.|O/ b loose
/> .|.|O.*> .|O.*> O/ b loose
h
b
: win
s/$/\
\
You win!!/
q
: loose
s/$/\
\
I win!!/
q
: draw
s/$/\
\
It's a draw!!/
q
: playA
s/> /> O/
b finished
: playB
s/| |/|O|/
b finished
: playC
s/\(|.|\) /\1O/
b finished
Przykład zaczerpnięty ze strony: http://sed.sourceforge.net/local/games/tictactoe.sed.html.
Tomasz Zin
Wyrażona powyżej opinia jest prywatnym poglądem autora wypowiedzi. Korzystasz na własną odpowiedzialność.
