De Stream Editor - sed

De streameditor, sed, is een niet-interactieve editor die ontworpen is om efficient, willekeurig grote, bestanden te bewerken. Meestal wordt sed gebruikt om een bewerking, invoer -> uitvoer, uit te voeren, m.a.w. als filter.

Analoog aan de shell wordt sed vaak aangestuurd van uit een "sed-script", waar de edit-opdrachten in staan.

plaatje

Bijvoorbeeld een file met:

Aanroep: sed -f sed-script

Het kan echter ook door het script als (eerste) argument mee te geven.

Als er meerdere edit-commando's nodig zijn, worden ze elk vooraf gegaan door -e (van expressie):

Opties voor sed

-n
Normaliter worden alle invoer regels doorgeven/afgedrukt. Je zou kunnen zeggen dat achteraan het sed-script impliciet een 'p' (print) opdracht staat. De -n optie echter beperkt de uitvoer tot alleen die regels waar een expliciete print opdracht voor gegeven is.
-e expr
Beschouw expr als een edit opdracht.
-f file
Haal edit opdrachten uit file file.

NB -e en -f mogen meermaals gecombineerd worden !

is dus een geldige aanroep, die 3 "deel" scripts combineert.

Formele beschrijving van een editopdracht

Formeel ziet elke edit opdracht er uit als:

Anders gezegd:

Reguliere Expressies

In de context van sed staat het begrip adres voor regelaanduidingen. Dat kunnen gewoon regelnummers zijn, bijvoorbeeld: 1,3d, waarbij $ symbolisch is voor de "laatste" regel (Je kan helaas niet rekenen). Het kunnen ook context adressen zijn, d.w.z. expressies van de vorm /re/, waarbij re ("reguliere expressie") een eigenschap van de regel aangeeft. Een reguliere expressie is opgebouwt uit:

^
Als ^ als eerste in de expressie staat is het synoniem met het begin van de regel (anders "zichzelf").
$
Als $ als laatste in de expressie staat is het synoniem met het einde van de regel (anders "zichzelf").
.
De . (punt) is synoniem voor een willekeurig karakter.
[xyz]
Is synoniem voor: een van de karakters uit de verzameling xyz.
Voorbeeld: [aeiou] staat voor "een klinker".
Bij veel versies van sed mag je een reeks ook verkort opschrijven als: eerste_teken-laatste_teken, b.v. [a-z0-9].
Als de ^ onderdeel van de verzameling moet zijn moet je even uitkijken want ...
[^xyz]
Is synoniem voor: een van de karakters NIET uit de verzameling xyz (m.a.w. het "complement").
Voorbeeld: [^aeiou] staat voor "alles behalve een klinker".
Als de ^ zelf onderdeel van de verzameling moet zijn moet je 'm niet als eerste schrijven.
x
Elk ander karakter x staat voor "zichzelf".
\(re\)
Staat voor de re tussen de haken, maar ...
\N
waarbij N een cijfer in de reeks 1 t/m 9 is is. Dit staat voor de tekst die correspondeert met de Ne\(...\), eerdere, expressie.
Voorbeeld: \([aeiou]\)\1 staat voor "een klinker" gevolgd door "dezelfde klinker".
re*
Een reguliere expressie re, gevolgd door * staat voor 0 of meer keer die re. Dus:

Als je een van de speciale tekens nodig hebt als "zichzelf" kan je dat aangeven door er een \ (backslash) voor te zetten.

Adressen

Het aantal adressen bepaalt wanneer de editfunctie toegepast wordt:

aantal van toepassing op invoer regel(s)
0 elke regel
1 elke regel die correspondeert met adres
2 vanaf start tot/met eerstvolgende eind (en dan weer herhaald !)

Voorbeelden:

s/A/a/g
Vervang elke 'A' door 'a' in elke regel.
/ab/d
Delete elke regel waar ab in staat.
1,3d
Delete regel 1 t/m 3.
$d
Delete de laatste regel ($-4 o.i.d. werkt helaas niet).
/aa/,/bb/d
Delete alle regels, steeds te beginnen met de regel waar aa instaat, tot en met de regel waar bb instaat. Herhaal dat "recept" zolang er invoer is.

Gewone Edit Opdrachten

Hieronder staan een aantal sed-opdrachten. Er voor staat tussen haakjes hoeveel addressen mogelijk zijn.

De meeste opdrachten die sed kent zijn analoog aan de opdrachten die b.v. de ex editor kent.

(1) a\
tekst
"Append"; voeg de regel(s) toe aan de uitvoer. Als het er meer dan één zijn moeten ze allemaal op \ (backslash) eindigen (behalve de laatste).
(1) i\
tekst
"Insert"; Analoog aan "append".

Het verschil tussen a en i zit in de volgorde als de invoer regel ook afgedrukt zou worden.

(2) c\
tekst
"Change"; Analoog aan "append/insert". De huidige invoerregel vervalt helemaal.
(2) d
"Delete"; Gooi de regel weg.
(2) n
"Next"; lees meteen de volgende invoer regel. De volgende edit commando's hebben nu dus betrekking op de nieuwe invoer regel. Wat er met de vorige gebeurt wordt bepaalt door de -n optie van sed; zonder -n wordt ze nog afgedrukt, anders niet.
(2) s argumenten
"Substitute"; Er zijn in principe drie argumenten: Het pattern is analoog aan de adres expressie maar nu mag elk willekeurig teken als "delimeter" (scheiding) op treden (dus niet alleen de /)
Het replacement argument geeft de vervangende tekst, tot aan de volgende delimiter (totaal dus 3). Het kan twee speciale tekens bevatten: De, optionele, opties kunnen zijn: Voorbeeld: s!studen!&en! vervangt de eerste 'student' in de regel door 'studenten'.
Voor de variatie nu eens '!' als scheidings-teken gebruikt.
Voorbeeld: s/Ab/CD/g vervangt alle (g-optie) 'Ab' door 'CD'.
NB Als er meerdere s commando's zijn kunnen de p en w opties meerdere "versies" van dezelfde invoer regel op leveren !

Invoer/Uitvoer Opdrachten

(2) p
"Print"; Druk de regel(s) af.
(2) w file
"Write"; Schrijf de regel(s) naar file.
(1) r file
"Read"; Lees extra regels van file en druk ze af. Is analoog aan de a en i opdracht.

Buffer Opdrachten

Elke ingelezen invoer regel komt in een "huidige invoer" buffer terecht en wordt daar bewerkt. Meestal wordt de gebufferde regel meteen weer weggeschreven. Deze invoer buffer kan echter meerdere invoer regels bevatten.

(2) N
"Next"; Lees de volgende invoer regel in en voeg die toe aan de invoer buffer. De vorige invoer regel(s) blijven dus ook in de invoer buffer, gescheiden door een \n (newline).
In de pattern expressie van de "substitute", kan je expliciet naar die \n refereren, b.v. de opdracht s/\n// maakt van de eerste 2 regels in de invoer buffer één regel.
(2) D
"Delete"; Gooi de eerste van de regels in de invoer buffer weg.
(2) P
"Print"; Druk de eerste van de regels in de invoer buffer af.

"Hold Space"

Naast de buffer waar 1 of meer invoer regels in staan heeft sed nog een z.g. "hold" buffer.

(2) h
"hold"; copieer de inhoud van de invoer buffer naar "hold-space" (replace).
(2) H
"Hold"; voeg de inhoud van de invoer buffer aan de "hold-space" toe (append).
(2) g
"get"; copieer de inhoud van de "hold" buffer naar de invoer buffer (replace).
(2) G
"Get"; voeg de inhoud van de "hold" buffer toe aan de invoer buffer (append).
(2) x
"Exchange"; wissel de inhouden van de invoer en de hold buffer om.

Diversen

(2) !...
"Do-not"; Inverteert de betekenis van de adres selectie.
voorbeeld: 1,3!d, regel 1 t/m 3 worden NIET gedelete.
voorbeeld: /aa/,/bb/!p, druk alle regels af BEHALVE de regels in de "reeks" /aa/ t/m /bb/
(2) {...
"Group"; De verzameling van opdrachten, tot aan een regel met alleen }, worden allemaal toegepast op de geselecteerde invoer regels. Een groep mag zelf weer een groep bevatten, enz.
(0) : label
"Mark"; Markeer een plaats in het sed script met het gegeven label, een naam van maximaal 8 karakters.
(2) b label
"Branch"; Spring naar de regel die gemerkt was met dat label. Een blanco label staat voor "einde-script".
(2) t label
"Test"; Als er een substitute gelukt is, spring dan naar label, ga anders gewoon verder met het sed-script.
(1) =
Drukt het regel nummer van huidige invoer regel af (de tekst regels van de a, i, c en r opdrachten hebben dus geen invloed).
(1) q
"Quit"; beëindig het sed-script.

Voorbeelden

Verwijder de 1-e en de laatste regel van de invoer

Als we een java of c++ file hebben kunnen we het //-commentaar verwijderen met:

aangenomen dat "//" niet ergens in een string stond.

Als we een pure tekst file hebben waarvan sommige regels op - eindigen vanwege woordafbreking, kunnen we dat weer ongedaan maken met:

Tekstueel:
Als: we een regel zien die op '-' eindigt:
dan: lees ook de volgende regel in
en: haal de '-' en de '\n' tussen de regels weg

Oefeningen

  • Bedenkt een sed-script, en de aanroep, die elke commentaar regel (dus alle '# ....' regels) elimineert uit een shell-script.
  • Bedenk een sed-script, en de aanroep, die elke oneven regel afdrukt.
  • Bedenk een sed-script, en de aanroep, die elke even regel afdrukt.
  • Bedenk een sed-script, en de aanroep, die alle even regels achter de oneven regels plakt.
  • wordt dus:

  • Geef een sed-script, en de aanroep, om de laatste twee velden van de volgende invoer vooraan te krijgen. Het is een gegeven dat die twee velden elk altijd 5 karakters groot zijn, verder is er niets van bekend.
  • moet worden:

  • Geef een sed-script, en de aanroep, die het omgekeerde van bovenstaande doet.
  • Tenslotte - voor de moedige studenten

    Maak een sed-script, "html.sed", die de feitelijke tekst uit een html file kan isoleren (anders gezegd, alle "html" "tags" elimineren).
    Voor wie niet weet hoe html in elkaar zit, bekijk de files van het vak inleiding unix dan zie je snel genoeg hoe dat in elkaar zit.

    In html files worden speciale karakters aangegeven met een bijzondere notatie, b.v. "é" (staat voor een é). Om die terug te vertalen naar "gewone tekst" zijn er sed-scripts beschikbaar iso8.sed en iso7.sed. Probeer eerst iso8.sed, als dat problemen met andere programma's op levert, gebruik dan iso7.sed.


  • Maak vervolgens een shell-script, "wft", dat een "woorden frequentie tabel" maakt, d.w.z. een tabel waarin voor elk woord staat hoe vaak het in de invoer voorkwam. Voor "woord" geldt de gangbare definitie (letters en/of cijfers), waarbij (voor het gemak) "1234" en "a13", etc, als woord beschouwd mogen worden. De tabel moet gesorteerd zijn op frequentie (hoog ... laag).

    Het script moet zo in elkaar zitten dat je het zowel kan aanroepen met 0 argumenten (dan leest het standaard invoer) of met een aantal filenamen (dan leest het die files). Er komt altijd één woorden frequentie tabel uit.

    Ga er van uit dat de invoer (file(s)) gewoon tekst bevatten (dus geen c++ code, o.i.d.). Enkelvoud/meervoud worden als aparte woorden beschouwd, idem vervoegingen, etc. Je hoeft ook geen rekening te houden met woord-afbreking.
    Denk wel aan het elimineren van leestekens, en dat in teksten woorden soms met een hoofdletter en soms met kleine letter geschreven worden (en dan wel als hetzelfde woord gezien moeten worden).
    PS Sommige programma's willen een kode zien i.p.v. \n (newline), die kode is \12 (staat ook in man ascii c.q. de file /usr/pub/ascii).


  • Maak tenslotte een shell-script, "hwft", die op zijn beurt de twee vorige gebruikt, zodat de invoer nu uit 1 of meerdere html files kan komen.

    Laat jouw hwft los op een paar van de .html files in de directory /hio/praktikum/i1/inl_unix. Bepaal in elke tabel de eerste 5 "ongewone" woorden (dus niet woorden zoals "de", "het", "voor", "achter", "je", etc). Als het goed is heb je nu per geval een aantal "trefwoorden" gevonden die kenmerkend zijn voor die documenten.

    Hint: Je zal naast sed ook gebruik kunnen maken van b.v. "tr", "sort" en "uniq". Soms meer dan 1 keer.

    Randvoorwaardes zijn:

  • Je mag nergens hulpfiles gebruiken (dus alleen "pipes"),
  • Je mag hooguit de beschikbaar gestelde sed scripts copieren,
  • Je mag geen awk gebruiken.

  • Klik hier t.z.t. voor de uitwerkingen.
    Laatst veranderd op: Mon Mar 9 15:37:37 MET 1998
    Please email any comments to:
    Ron Akkersdijk