Sed
sed
je skvelý nástroj na nahrádzanie výskytov reťazcov v textoch, ale aj pre mnoho iných, nečakaných manipulácií s textovými súbormi. Jeden z najprehľadnejších návodov nájdete na http://www.grymoire.com/Unix/Sed.html. Samozrejme, užitočná je aj infotex stránka v dokumentácii (info sed
)
Príkaz pre nahradenie
Najčastejšie používaným príkazom je s
, teda substitute. Má minimálne dva parametre: výraz, ktorý chceme nahradiť a výraz, ktorým nahradíme nájdený text. Školský príklad:
echo Milujem Windows | sed 's/Windows/Linux/'
Výstupom je Milujem Linux
.
Nástroj sed
podporuje aj tradičné špeciálne znaky typu \n
(nový riadok), \t
(tabulátor) atď.
echo Ahoj svet | sed 's/ /\n/'
Výstupom je
Ahoj
svet
Pozor na to, že sed
nahrádza štandardne len prvý výskyt na riadku. Ak chceme vykonať globálne nahradenie všetkých výskytov, použime príznak g
za nahradením
echo John Paul Ringo George | sed 's/ /\n/g'
Výstupom je
John
Paul
Ringo
George
Iný príklad
echo Terry Jones, Terry Gilliam | sed 's/Terry/T./g'
Výstupom je
T. Jones, T. Gilliam
Niekedy potrebujeme nahradiť len konkrétny, napr. druhý výskyt. Namiesto príznaku g
môžeme použiť poradové číslo výskytu na riadku. V príklade vymažeme druhého Terryho:
echo Terry Jones, Terry Gilliam | sed 's/Terry/T./2'
Výstup:
Terry Jones, T. Gilliam
Ak chceme vymazať výskyt slova z riadku, stačí nahradiť text prázdnym reťazcom a použiť globálne nahrádzanie
echo Terry Jones, Terry Gilliam | sed 's/Terry//g'
Výstupom je
Jones, Gilliam
Ak prekážajú medzery, stačí poupraviť hľadaný výraz (dodajme medzeru):
echo Terry Jones, Terry Gilliam | sed 's/Terry //g'
Niekedy chceme nájsť výraz a v nahradení ho použiť tak ako je, iba s nejakou predponou, či príponou. Ampersand &
vo výraze nahradenia obsahuje nájdený výraz. Obaľme všetkých Terryov hviezdičkami:
echo Terry Jones, Terry Gilliam | sed 's/Terry/*&*/g'
Vybrané výrazy môžeme uzavrieť do zátvoriek, čím vyznačíme skupiny a potom na ne vo vyhľadávacom výraze. Ak chceme z riadka označujúceho Java interfejs vyseknúť len jeho názov, môžeme ísť na to nasledovne:
echo 'public interface MyComparator {' | sed 's/public interface \([^ ]*\).*/\1/'
Nájsť všetky interfejsy je potom ľahé
find / -name *.java -exec sed -n 's/public interface \([^ ]*\).*/\1/p' {} \; 2> /dev/null
Analogicky vieme nahradiť cut
. Ak chceme vypísať domovské adresáre používateľov, tu je postupnosť krokov. Najprv zadefinujme premennú pre sprehľadnenie regexpu:
E="\([^:]*\)"
Potom ju radostne použime vo výraze, pričom využijeme shellovskú expanziu
sed 's/'$E:$E:$E:$E:$E:$E:.*'/\6/g' /etc/passwd
Príkaz q
(quit) a rozsahy riadkov
Nástroj sed
môžeme použiť ako lacnú náhradu head
u. Ďalším užitočným príkazom je q
(quit).
Jednoduché použitie načíta prvý riadok zo súboru a skončí beh sed
u
sed q /etc/passwd
Väčšia zábava bude s použitím rozsahov. Každý príkaz môže mať rozsah, teda špecifikáciu riadkov, na ktoré sa použije. Veľmi často sa používa rozsah v podobe čísiel riadkov: Prvé tri súboru (teda ekvivalent head -n3
) vypíšeme
sed 3q /etc/passwd
Priebeh je nasledovný: vo chvíli, keď sed
naďabí na tretí riadok, vykoná uvedený príkaz – v našom prípade skončí.
Príkaz p
(print)
V štandardnom režime funguje sed
tak, že každý načítaný riadok automaticky vypisuje bez ohľadu na to, či sa naň aplikoval nejaký príkaz alebo nie. Niekedy však chceme tlačiť len riadky, na ktoré sa príkaz skutočne použil.
Najjednoduchšie použitie príkazu print zduplikuje každý riadok:
echo Hello World | sed p
Oveľa častejšie sa p
používa v kombinácii s prepínačom -n
. Ten spôsobí, že sed
nebude automaticky vypisovať žiadne riadky, ale vypíše tie, na ktoré sa aplikuje p
.
Tretí riadok vieme vypísať pomocou
sed -n 3p /etc/passwd
Podobne môžeme iným spôsobom napodobniť head
sed -n 1,10p /etc/passwd
Ak chceme použiť rozsah ,,od piateho riadku až do konca”, môžeme pre posledný riadok použiť zástupný znak $
(keďže je to špeciálny znak shellu, obaľme príkaz do apostrofov):
sed -n '5,$p' /etc/passwd
Možno vám napadne, že či sa nedá napodobniť aj tail
. Na to však potrebujeme kombináciu viacerých príkazov shellu. V príklade vypíšeme posledných 10 riadkov súboru:
L=$(wc -l < /etc/passwd); sed -n $((L-9))',$p' /etc/passwd
Do premennej L
si poznamenáme počet riadkov súboru, v aritmetickom prostredí odčítame od počtu riadkov deväť, čím získame číslo desiateho riadku od konca a vypíšeme riadky medzi L a koncom súboru.
,,Sprehľadniť” skript možno ešte dodatočnou premennou s názvom súboru
F=/etc/passwd; L=$(wc -l < $F); sed -n $((L-9))',$p' $F
Príkaz print v náhrade grep
u
Namiesto rozsahu riadkov môžeme použiť príkaz na riadky, ktoré spĺňajú regulárny výraz a tým nahradiť grep
Vypísať riadok z /etc/passwd
, ktorý obsahuje informácie o rootovi môžeme:
sed -n /root/p /etc/passwd
Nezabudnime na parameter -n
, v opačnom prípade sa vypíšú všetky riadky, ale riadok s rootom dvakrát.
Ak chceme vypísať všetkých používateľov, ktorí sa nemôžu prihlásiť (majú shell /bin/false
), musíme escapenúť špeciálny znak lomky (pretože tá oddeľuje parametre príkazu s
) a keďže samotná lomka je špeciálny znak v shelli, uzavrime príkaz do apostrofov:
sed -n '/\/bin\/false/p' /etc/passwd
Tento štýl lomiek sa familiárne nazýva plánkový plot a je pomerne neprehľadný. Našťastie sed
umožňuje nastaviť pre úlohu oddeľovača ľubovoľný znak. Ak použijeme napríklad rúru, príkaz sa sprehľadní:
sed -n '|/bin/false|p' /etc/passwd
Mazanie (delete) pomocou d
Na mazanie riadkov zo vstupu slúži príkaz d
. Filozofia je jasná: stačí uviesť rozsah riadkov, na ktoré sa d
použije.
Prvých troch používateľov z /etc/passwd
zmažeme
sed 1,3d /etc/passwd
Alternatívne môžeme mazať pomocou vzorky a regulárneho výrazu. Používateľa novotnyr
zmažeme:
sed '/^novotnyr/d' /etc/passwd
Regulárny výraz začínajúcu zobákom znamená ukotvenie k začiatku riadku.
Vkladanie riadkov (insert) pred výraz pomocou i
Príkaz i
vloží riadok pred aktuálny riadok. Ak máme zdrojový súbor v Jave a pred každý začiatok deklarácie triedy chceme vložiť komentár z niekoľkých mriež, vieme na to presne použiť tento príkaz.
Komentár vygenerujeme napr. týmto one-linerom
unset COMMENT; for i in $(seq 40);do COMMENT=$COMMENT#; done; COMMENT='/*'$COMMENT'*/'
Nahradenie v sed
e:
sed /class/i$COMMENT HelloWorld.java
Výsledkom bude súbor s riadkami, kde pred triedou bude ilustratívna čiara
#############################
public class HelloWorld {
Vkladanie riadkov (append) pred výraz pomocou a
Analogicky k insertu funguje append, ktorý však pridáva za nájdený riadok.
Zmena riadkov (change) pred výraz pomocou c
Príkaz c
zamieňa kompletné riadky novým obsahom. Čisto akademický príklad vygeneruje na každý riadok samostatné slovo a následne každý tretí riadok nahradí reťazcom TRETI
echo 'Hello World How Are You?' | tr ' ' '\n' | sed '1~3c TRETI!'
Využívame tu špeciálny rozsah riadkov s krokom: 1~3
znamená ,,počnúc prvým riadkom aplikuj príkaz na každý tretí riadok”, teda znamená to rozsah 1, 4, 7…
Číslovanie riadkov (príkaz =
)
Očíslovať riadky môžeme pomocou =
, s drobným nedostatkom: čísla sa vypisujú na samostatné riadky.
Napr. je jednoduché nasimulovať wc -l
. Stačí zobrať za rozsah posledný riadok ($
), a zakázať automatický výpis riadkov a explicitne vypísať číslo posledného riadka.
sed -n $= /etc/passwd
Príkaz N
(pridaj ďalší riadok k aktuálnemu)
Ak chceme mať čísla riadkov pohromade s riadkami, je to o niečo zložitejšie. Príkaz N
pridá k aktuálnemu riadku so vzorkou znak oddeľovača riadka a dolepí zaň obsah nasledovného riadka. Filozofia bude nasledovná: v prvom behu sed
u vygenerujeme čísla riadkov a obsahy a v ďalšej rúre vezmeme riadok, prilepíme k nemu znak n
a nasledovný riadok (pomocou N
). Teraz stačí nahradiť oddeľovač riadkov tabulátorom a máme to.
Viacero príkazov v sed
e vieme veselo oddeľovať bodkočiarkou
sed = /etc/passwd | sed 'N;s/\n/\t/'