Motto:
We're peering into the origin of Unix here. I knew people at Bell Labs though I never worked there myself. The late Lee E. McMahon, the original author of ‚sed‘, was a fellow student at Rice University in the early 1970s before he went off to Bell Labs to write ‚sed‘.
Aha! I knew it! Having such a clumsy syntax was necessarily the result of some student project :)
[ mail.gnu.org, archiv konference autoconf-patches ]
Co je to sed
Program sed (stream editor) je určen pro dávkové zpracování textů. Je větší než grep, a podobně jako u grepu, pro využití sedu musíme vědět něco o regulárních výrazech a o programování v shellu. V tomto článku se věnuji klasickému starému sedu, ačkoliv se zmíním i o některých rozšířeních Gnu.
Použití sedu vypadá takto
sed [-n] [-e script] ... [-f script_file] ... [soubor ...]
nebo v koloně
cat "soubor" | sed [-n] [-e script] ... [-f script_file]
Sed čte ze souboru řádku po řádce, přičemž obyčejně funguje v následujícím cyklu:
- přečte řádku a interně si ji očísluje
- řádku vloží do svého pracovního prostoru
- na obsah pracovního prostoru postupně aplikuje sedscript, tj. posloupnost sedovských příkazů
- na závěr cyklu obsah pracovního prostoru pošle na standardní výstup
a tak dále až do konce souboru.
Pracovní prostor (pattern space, PS) je kus paměti, který může obsahovat jednu nebo více řádek. On a jeho bráška paměťový prostor (hold space, HS) tvoří jediné dvě proměnné, do kterých můžeme něco uložit. Implicitní výstup podle bodu d) lze potlačit parametrem -n
Posloupnost sedovských příkazů se zadává přímo z řádky pomocí -e nebo se čte ze souboru pomocí -f . Obě volby se mohou opakovat a míchat mezi sebou, rozhodující je pořadí na řádce.
Pomocí obou způsobů je zadána posloupnost příkazů, kterou dále nazývám sedscript. Maximální počet příkazů sedscriptu býval sto. Také je možné volby -e -f vynechat úplně, potom sed jako své příkazy intepretuje první argument. Jako obvykle, pokud sed nedostane jméno textového souboru, čeká na data ze standardního vstupu.
Příklad:
sed ' ' text dělá totéž, co cat text.
Dále předpokládejme ukázkový desetiřádkový textový soubor text obsahující:
radka 1 radka 2 start radka 3 radka 4 stop radka 5 radka 6 start-stop radka 7 radka 8 radka 9 radka 10
V příkladech se objeví příkaz sedu p, tj. tisk na standardní výstup.
Adresování řádků
Osamělý sedovský příkaz zareaguje na každou řádku, která přijde. Platnost sedovského příkazu lze omezit jednou adresou nebo rozsahem dvou adres oddělených čárkou.
Adresy jsou dvojího druhu:
- číslo řádku, speciálně $ znamená poslední řádku vstupu (pozor na shellovskou expanzi)
- regulární výraz (RE)
- Regulární výrazy se zadávají uzavřené mezi lomítka, např. /^Aug.sshd.Failed/
Eskejpování lomítek uvnitř RE se lze vyhnout alternativním zápisem:
\x^Aug.*sshd.*Failedx
kde x může být libovolný nekontrolní znak.
Pokud je před příkazem uvedena jedna adresa, bude příkaz aplikován na každou vyhovující řádku.
V případě dvou adres příkaz funguje na rozmezí (blok) řádek od,do včetně. Platnost adresy nebo rozsahu adres lze obrátit připojením vykřičníku. Vykřičník můžeme v popisu adresy umístit pouze na úplný konec, takže zápis sed -n '/start/!,10p'
text je špatně.
Posledním kouzlem s adresami jsou složené závorky. Díky nim lze pro danou adresu sdružit více příkazů do bloku.
#!/usr/bin/sh sed -n '/start/{ p /stop/{ p } } ' text $ ./s.sh radka 2 start radka 6 start-stop radka 6 start-stop
Otvírací závorka musí ležet na konci řádky a uzavírací závorka na samostatné řádce. Na příkladu vidíme, že bloky příkazů lze vnořovat.
Přehled příkazů
Nejprve si uvedeme přehled všech příkazů. Není jich mnoho, ale protože některé z nich mají vedlejší efekty, skládání sedovského skriptu připomíná hru Tetris.
#n | na první řádce scriptu znamená totéž co parametr -n |
# | je komentář |
[1adresa]q | ukonči se (implicitní výstup se provede) |
[1adresa|rozsah]p | vytiskni obsah pracovního prostoru plus znak nové řádky |
[1adresa]= | vytiskni číslo aktuální řádky a odřádkuj |
[1adresa|rozsah]y/abc/xyz/ | zaměň znaky pracovního prostoru |
[1adresa|rozsah]s/vzor/náhrada/ | příznaknahraď v pracovním prostoru |
[1adresa|rozsah]d | smaž pracovní prostor, pokračuj další řádkou od začátku sedskriptu |
[1adresa|rozsah]l | vytiskni obsah pracovního prostoru a místo speciálních znaků tiskni jejich ascii kód |
[1adresa|rozsah]n | do pracovního prostoru načti další řádku, předchozí obsah zapomeň |
[1adresa]a\ | vytiskni za append |
[1adresa]i\ | vytiskni před insert |
[1adresa|rozsah]c\ | změň, vytiskni a začni nový cyklus change |
[1adresa]rsoubor | vytiskni obsah souboru |
[1adresa|rozsah]wsoubor | zapiš pracovní prostor do souboru |
[1adresa|rozsah]N | do pracovního prostoru přidej znak \n plus další řádku ze vstupu, když žádná další řádka není k dispozici, skonči |
[1adresa|rozsah]D | v pracovním prostoru odmaž jednu řádku a skoč na začátek sedskriptu, pokud je pracovní prostor prázdný, přečti do něj další řádku |
[1adresa|rozsah]P | z pracovního prostoru vytiskni jednu řádku |
[1adresa|rozsah]h | ulož obsah pracovního prostoru do paměťového prostoru |
[1adresa|rozsah]H | do paměťového prostoru připoj znak \n plus obsah pracovního prostoru |
[1adresa|rozsah]g | ulož obsah paměťového prostoru do pracovního prostoru |
[1adresa|rozsah]G | do pracovního prostoru připoj znak \n plus obsah paměťového prostoru |
[1adresa|rozsah]x | vyměň mezi sebou obsahy pracovního a paměťového prostoru |
:návěští | je návěští |
[1adresa|rozsah]bnávěští | skoč na návěští |
[1adresa|rozsah]tnávěští | podmíněný skok, pokud byla úspěšná poslední náhrada pomocí s příkazu |
Příkazy mohou být uvedeny buď zvlášť, na samostatných řádkách, nebo mohou být odděleny středníkem. Středníkem nelze oddělovat
příkazy :,b,t pracující s návěštími.
Příkaz [1adresa]q
Sed sice hned skončí, ale bez -n volby se obsah pracovního prostoru s koncem skriptu vytiskne.
Příkaz [1adresa|rozsah]p
Používá se většinou spolu s volbou -n .
$ sed -n '/stop/p' text radka 4 stop radka 6 start-stop
Příkaz =
Vytiskne obsah čítače řádek. Čísla se tisknou na samostatné řádky a počítají se dohromady přes všechny zpracovávané soubory. Podobně dohromady pro všechen vstup funguje i adresa poslední řádky $.
$ sed -n '$='text text 20
Příkaz [1adresa|rozsah]y/abc/xyz/
Slabší varianta unixovského tr. Neumí rozsahy ani třídy, všechny znaky je nutné vypsat.
Příkaz [1adresa|rozsah]s/vzor/náhrada/příznak
Nahrazování v pracovním prostoru. Vzor je regulární výraz, ve kterém se mohou používat sdružovací \( ) a intervalové závorky \{n,m\}. Plus a otazník jsou obyčejné znaky. Místo obyčejného lomítka lze jako oddělovač za s použít libovolný nekontrolní znak, který se ale musí uvnitř RE eskejpovat. V náhradě mají kromě zpětného lomítka speciální význam také:
& … značí řetězec odchycený vzorem
\n … značí podřetězec odchycený ve vzoru pomocí sdružovacích \( ) závorek
Zpětná reference \n tvoří jednu z hlavních předností sedu. Nemá ji ani klasické awk, tj. bez funkce gensub. Ve vzoru lze používat sekvenci \n pro znak nového řádku. Příznaky mohou být číslo – reaguje pouze na n-tý výskyt vzoru, p jako print, g jako global, nebo w file pro zápis do souboru.
Příkaz [1adresa|rozsah]d
Smaže pracovní prostor, ignoruje zbytek sedscriptu a zahájí nový cyklus, tj. načtení další řádky vstupu podle bodu a).
Příkaz [1adresa|rozsah]l
Vytiskne obsah pracovního prostoru a místo speciálních znaků tiskne jejich ascii kód.
Příkaz [1adresa|rozsah]n
Podle volby -n obsah pracovního prostoru buď vytiskne, nebo ne. Poté do pracovního prostoru načte další řádku a současně zvýší o jedničku čítač řádek (=). Pokračuje v provádění sedscriptu.
$ sed -n -e ‚n;p‘ text
Tiskne jen sudé řádky.
Příkaz [1adresa]a\
append text
Text se pro daný řádek přidá až za sedovský výstup dané řádky.
Příkaz [1adresa]i\
insert test
Text se pro daný řádek vloží do sedovského výstupu.
Příkaz [1adresa|rozsah]c\
change text
Pro adresu nebo rozsah adres vytiskne „change text“ , ignoruje následující příkazy sedscriptu a pokračuje novým cyklem.
Příkaz [1adresa]r soubor
Poté, co načte a zpracuje adresovanou řádku, vytiskne obsah souboru. Obsah souboru se neukládá do paměťového prostoru a tiskne se i při volbě -n.
Příkaz [1adresa|rozsah]w soubor
Pro dané adresy zapiš pracovní prostor do souboru. Soubor je založen/přepsán při startu sedu, poté se do něj až do zpracování všech datových souborů připisuje.
Příklad
sed -n -e ‚s/^[^0–9]\([0–9]).*/\1/ ;w soubor‘ text text
V aktuálním adresáři se vytvoří soubor obsahující dvacet řádků s čísly 1,2,…,10,1,2,3,…,10
Příkaz [1adresa|rozsah]N
Do pracovního prostoru přidej znak \n plus další řádku ze vstupu. Pokud žádná další řádka není k dispozici, skonči ihned. Často se používá ve tvaru $!N , tj. připoj další řádek, jen pokud nejsme na posledním řádku.
Příkaz [1adresa|rozsah]D
V pracovním prostoru odmaž jednu řádku odleva až do prvního výskytu \n včetně a skoč na začátek sedskriptu. Pokud je pracovní prostor prázdný, přečti do něj další řádku ze vstupu.
Příkaz [1adresa|rozsah]P
Z pracovního prostoru vytiskni jednu řádku, tj. všechno zleva až do znaku \n .
Příkaz [1adresa|rozsah]h
Ulož obsah pracovního prostoru do paměťového prostoru. Paměťový prostor byl původně určen pro uchovávání několika málo řádek a jeho maximální velikost je třeba vyhledat v manuálových stránkách (hpux sed 8192 bytů). Gnu implementace toto omezení nemá.
Příkaz [1adresa|rozsah]H
Na konec paměťového prostoru připoj znak \n plus obsah pracovního prostoru.
Příkaz [1adresa|rozsah]g
Ulož obsah paměťového prostoru do pracovního prostoru (protějšek malého h).
Příkaz [1adresa|rozsah]G
Na konec pracovního prostoru připoj znak \n plus obsah paměťového prostoru (protějšek velkého H).
Příkaz [1adresa|rozsah]x
Vyměň mezi sebou obsahy pracovního a paměťového prostoru.
Příkaz :návěští
Jako označení návěští se považují znaky za dvojtečkou do konce řádky. V jednořádkových sedovských skriptech je nutné příkaz uvést jako samostatnou -e ‘: navesti‘ volbu.
Příkaz [1adresa|rozsah]bnávěští
Skoč na návěští. Pokud návěští není uvedeno, skáče se na konec sedscriptu.
Příkaz [1adresa|rozsah]tnávěští
Podmíněný skok, pokud v právě zpracovávaném řádku došlo od předchozího použití t příkazu k alespoň jedné úspěšné náhradě pomocí s substituce. Jedná-li se o první použítí t příkazu pro daný řádek, počítají se s příkazy od začátku cyklu.
Bez uvedeného návěští se skáče na konec sedscriptu.
Příště se podíváme na příklady použití.