Kyllästyin tänään editoimaan käsin blog.html
tiedostoa aina kun halusin lisätä siihen uuden aiheen. Nopeastihan sen sorkkiminen toki vim-tekstieditorilla käy, mutta tämä operaatio olisi pitänyt automatisoida jo heti blogini alkuvaiheessa vuosia sitten.
Luon tavallisesti blogini läppärilläni, jossa pyörii myös Apache www-serveri sivujen esikatseluun tarjoamista varten. Läppärilläni komento bash --version
kertoo versioksi GNU bash, version 4.4.19(1)-release (x86_64-redhat-linux-gnu)
. Mutta kolttonen.fi aktivistisivustoni sijaitsee Internetissä sellaisella www-palvelimella, jossa on vanhassa 32-bittisessä Intel-arkkitehtuurissa pyörivä GNU bash, version 4.3.30(1)-release
.
Merkitsin koodini kommentteihin, että tämä shelliskripti vaatii Bash 4.2 version tai uudemman toimiakseen, sillä käytin siinä merkkijonojen käsittelyyn syntaksia, jota vasta Bash 4.2 tukee. Sen uudempia ominaisuuksia en käyttänyt. Mutta nyt tuo merkintäkin on sitten virheellinen.
Kerron bugista kohta lisää. Skriptini on lyhyt ja se on tällainen:
#!/bin/bash # # Author: Kalevi Kolttonen 2018-06-13 <kalevi@kolttonen.fi> # # REQUIRES: Bash 4.2 or newer # # INPUT: blog entry file such as 2018-06-13b.html # OUTPUT: to stdout if [ $# -ne 1 ]; then printf "%s usage: %s infile.html\n" $(basename $0) $(basename $0) >/dev/stderr exit 1 fi INFILE=$1 if [ ! -f $INFILE ]; then printf "%s error: %s not found or is not a regular file\n" $(basename $0) $INFILE >/dev/stderr exit 2 fi # Get the topic, ignore other lines containing h2, remove h2 tags topic=$(grep '<h2>' $INFILE | head -1 | sed -e 's/<h2>//' -e 's_</h2>__') # Remove leading directory names INFILE=${INFILE##*/} # Remove .html file name suffix date=${INFILE//.html} # Remove a trailing alphabetic character if [[ $date =~ ([[:alpha:]]$) ]]; then date=${date::-1} fi OLDIFS=$IFS IFS=- read year month day <<< $date IFS=$OLDIFS # Remove leading zeroes from day and month if [[ $day =~ (^0) ]]; then day=${day:1} fi if [[ $month =~ (^0) ]]; then month=${month:1} fi printf "<p><a href=\"%s\">%d.%d.%d %s</a></p>\n" $INFILE $day $month $year "$topic"
Läppärilläni skripti toimii oikein:
Mutta www-serverillä päivämäärä onkin nurinpäin! Katso itse:
En huomannut tätä bugia skriptini ensimmäisellä versiolla, koska se oli toteutettu eri tavalla. Siinä alkuperäisessä purin date
-muuttujan osiinsa seuraavasti:
Kutsuin siis tyypilliseen Unix-tyyliin ulkoisia komentorivityökaluja eli tässä tapauksessa komentoa cut
. Bashia käytettäessä komento echo
ei minun tietääkseni kutsu ulkoista /bin/echo
binääriä, vaan Bash osaa turvautua sen sisäiseen toteutukseen tehokkuussyistä. Tuon asian varmistaa komento:
Se kertoo mitkä komennot on toteutettu Bash-komentotulkissa sisäänrakennettuna.
Minulla oli aikaa ja jostain syystä ajattelin, että on vähän tylsää tässä tapauksessa kutsua ulkoista cut
-binääriä kolme kertaa kun merkkijonon parsinta onnistuisi helposti ihan komentotulkin sisälläkin ilman tarvetta luoda ulkoisia apuprosesseja. Shellin sisäinen parsinta on tehokkaampaa, mutta käytännössä tällä asialla ei ole merkitystä. Skripti menee läpi silmittömän nopeasti vaikka shelli loisikin cut
-prosessin kolmesti. Lisäksi tätä työkalua ei ajeta useasti missään tiukassa luupissa, vaan sitä käytetään kerran tai pari vuorokaudessa. Siitä huolimatta kikkailin huvikseni hieman Bashin IFS
eli Input Field Separator muuttujalla ja parsin date
-muuttujan sisällön osiinsa yksinkertaisella komentotulkin sisäisellä read
-komennolla:
Tuossa siis määrittelin, että tavuviiva on kenttäerotin tästä eteenpäin, sillä date
on muodossa 2018-06-13
. Huomaa myös, että ennen IFS
-muuttujan asettamista otin vanhan arvon talteen OLDIFS
-muuttujaan. Niin pitää aina toimia ennen IFS
-muuttujaan kajoamista, sillä parsimisen jälkeen on ensiarvoisen tärkeää, että IFS
palautetaan heti takaisin alkuperäiseen arvoonsa. Ellei näin toimita, komentotulkin toiminta menee takuuvarmasti jossain vaiheessa mystisillä tavoilla aivan pieleen!
Bashin sisäisellä read
-komennolla voi parsia myös tiedostoista tulevia syötteitä. Silloin käytetään Unixin one-entry-per-line dataformaattiparadigmaa ja syntaksi on vaikka esimerkiksi näin:
while read foo bar baz; do echo $foo $bar $baz done < inputfilename
Näin voidaan välttyä kutsumasta esimerkiksi awk
-ohjelmointikieltä shelliskriptistä käsin. Tällöin muistaakseni kuitenkin on se ongelma, että while
-luupin alussa julistetut muuttujat näkyvät vain luupin sisällä. Ne eivät ole voimassa luupin jälkeen tulevassa koodissa.
LISÄYS YLLÄ OLEVAAN BLOGIKIRJOITUKSEEN 2018-06-15: Jaahas. Speksasin sitten skriptini väärin. Eihän siinä blog.html
tiedostossa koskaan olekaan otettu pois nollia päiväyksistä päivän tai kuukauden kohdalla. Muokkasin siis skriptiä vielä lyhyemmäksi ja nyt nollia ei poisteta.