page header

Werken met Github


Geplaatst door tonk op 2013-05-13 13:32:48 | Permanente link | Categorie: Systeembeheer, Tips and Tricks, Programmeren | Reacties: 0

Werken met Github

Al enige tijd werk ik met Github, een site waar je als open source ontwikkelaar, gratis, je projecten kunt hosten.

Ik heb daar in de loop der jaren al wat projecten op gezet die, natuurlijk, door iedereen vrij te gebruiken zijn.

Sinds enige tijd ben ik ook een van de, hobbymatige, ontwikkelaars van het Ansible configuratie tool en ik heb ook al een aantal patches ingediend.

Maar bij het indienen van die patches ontstaat vaak een probleem. Aangezien git het beleid heeft om vaak te committen (en ik dat ook trouw doe), blijkt het erg onhandig om dit soort patches op een goede manier in te dienen. Het verzoek van de beheerder van de hoofdtak zal dan ook zeker zijn on squashed commits, het tot een commit samengevoegde commits, in te dienen. Dit gaat goed, totdat er een aanpassing gemaakt moet worden aan de al ingediende patch. Er blijken dan altijd conflicten te ontstaan, die weer lastig op te lossen zijn.

Na een tijd zoeken en proberen heb ik de volgende werkwijze ontwikkeld, die goed werkt:

  • Als je het nog niet hebt, maak dan een account aan op github
  • Zorg dat git goed geconfigureerd is:

    git config --global user.name 'Ton Kersten'
    git config --global user.email 'Ton.Kersten@ATComputing.nl'
    
  • Kopieer het gewenste project op github naar je eigen account, oftwel "Maak een vork"

  • Kloon deze vork naar je eigen PC om zelf wijzigingen aan te brengen

    git clone https://github.com/tonk/ansible
    
  • In de meeste gevallen is het wel aan te raden om eerst een nieuwe tak te starten, omdat je in github slechts 1 patch-aanvraag per tak mag hebben.

    git checkout -b nieuwetak
    
  • Nu kun je allerlei aanpassingen maken, committen, testen en verder alles wat je normaal genomen in een ontwikkeltraject doet.

  • Als je nieuwe optie of bugfix de ultieme status bereikt heeft, dan kun je deze opsturen naar je eigen github repository. Maar eerst moet je alle tussentijdse commits samenvoegen tot een commit

    git checkout master
    git merge --squash -s subtree --no-commit -m 'De ultieme patch' nieuwetak
    
  • en bevestig deze samengevoegde set

    git commit -m 'De ultieme patch' -a
    
  • en stuur hem op

    git push origin master
    

Nu is de patch aangeland in de github repository. Via de web interface kun je nu een pull request sturen naar de beheerder van de hoofd repository van het softwareproject. Deze zal de patch beoordelen en eventueel accepteren.

Maar, wanneer het noodlot en de beheerder wil dat je nog wat veranderingen aan je patch aanbrengt, dan moet je met wat zaken rekening houden. De kans is namelijk vrij groot dat het project op het internet gewoon doorontwikkeld is en dus niet meer in lijn is met je eigen, lokale, repository. Wanneer je nu gewoon een update (git pull) zou doen, dan ontstaat er een conflict tussen je eigen aanpassingen en de aanpassingen op het web. Hier dien je dus terdege rekening mee te houden.

De juiste manier is dan ook om je lokale boom te updaten en daarna je eigen veranderingen hier weer overheen te spoelen. Dit klinkt omslachtig, maar kan met een commando

git pull --rebase

Als je nu weer een checkout doet van je eigen tak, dan kun je daar verder in ontwikkelen, zonder dat er conflicten ontstaan.

git checkout nieuwetak

en na het maken van de gewenste aanpassingen weer

git checkout master
git merge --squash -s subtree --no-commit -m 'De ultieme patch MK-II' nieuwetak
git commit -m 'De ultieme patch deel 2' -a
git push origin master

Het is hierna niet nodig om een nieuw pull request te sturen, omdat github aan de commits kan zien dat dit bij de vorige hoort. Er zal dan ook automatisch een signaal worden gestuurd naar de beheerder van de hoofdtak.

Op deze manier is het mogelijk om met grote groepen ontwikkelaars, gezamenlijk aan een project te werken, zonder in elkaars vaarwater te zitten.

Als je in het bestand ~/.gitconfig het volgende opneemt

[alias]
    timeline = log --graph \"--pretty=format:%C(192)%h%Creset by %C(bold magenta)%an%Creset (%ar)%C(182)%d%Creset%n%s%n%b\" --all

kun je met het command

git timeline

heel mooi zien hoe de takken verlopen.

Een klein voorbeeld uit de Ansible boom is

* |   611705d by Michael DeHaan (2 days ago)
|\ \  Merge pull request #2891 from glensc/make-nosetests
| | | make path to nosetests executable configurable
| * | a067877 by Elan Ruusam<C3><A4>e (2 days ago)
| | | make path to nosetests executable configurable
| | | this is to make use python2 when nosetests points to python3:
| | | 
| | | make NOSETEST=nosetests-2.7 tests
| | |      
* | |   2e2226a by Michael DeHaan (2 days ago)
|\ \ \  Merge pull request #2889 from caredotcom/newrelic_deployment_notification
| | | | newrelic_deployment notification module
| * | | 5e3ccc3 by Matt Coddington (3 days ago)
| |/ /  newrelic_deployment notification module
| | |      
* | |   de7829b by Michael DeHaan (2 days ago)
|\ \ \  Merge pull request #2888 from fabulops/campfire_notification
| | | | Campfire Notification Module
| * | | cebdcaa by Adam (3 days ago)
| |/ /  Campfire Notification Module
| | |      
* | |   cfe86be by Michael DeHaan (2 days ago)
|\ \ \  Merge pull request #2887 from caredotcom/flowdock_notification
| | | | flowdock notification module
| * | | 22ca463 by Matt Coddington (3 days ago)
| |/ /  flowdock notification module
| | |      
* | |   ec18467 by Michael DeHaan (2 days ago)
|\ \ \  Merge pull request #2886 from fesplugas/devel
| | | | Fixed Typo
| * | | 5b6087c by Francesc Esplugas (3 days ago)
| |/ /  s/temlpate/template

Onderstaand script laat op eenvoudige wijze de hierboven beschreven werkwijze zien, door een omelet te bakken ;-)

#!/bin/bash
# vi: set sw=4 ts=4 ai:

#- Redirect stderr to stdout (just so I can use '| less')
exec 2>&1

width=80
l="$(printf "%-${width}s" "")"
l="${l// /-}"

TOP="$(pwd)"

say()
{   printf -- "\n--- %s %s\n" "${*}" "${l}" | cut -c 1-${width}
}

gitcmd()
{   printf -- "\n--- git %s %s\n" "${*}" "${l}" | cut -c 1-${width}
    git "${@}" 2>&1 | sed 's/^/        /'
    # printf -- "${l}\n"
}

rm -rf upstream developer_1 developer_2

mkdir upstream
cd "${TOP}/upstream"
say 'Creating bare repository'
gitcmd --bare init

say 'Clone into developer_1'
cd "${TOP}"
gitcmd clone upstream developer_1
cd "${TOP}/developer_1"

say 'Adding eggs'
echo 'You need eggs' > eggs
printf "\n"
gitcmd add eggs
gitcmd commit -m 'Eggs are needed for an omelette' eggs

say 'Creating omelette branch'
gitcmd checkout -b omelette

say 'Adding chives'
echo ' And chives are nice' > chives
gitcmd add chives
gitcmd commit -m 'Chives are nice' chives
#
say 'Adding seasoning'
echo 'Pepper and salt is the bare minimum' > seasoning
gitcmd add seasoning
gitcmd commit -m 'Add some flavor' seasoning

say 'Returning to master'
gitcmd checkout master

say 'Squashing omelettes'
gitcmd merge --squash -s subtree --no-commit -m 'Start of the omelette' omelette

say '-> Committing omelettes'
gitcmd commit -m 'Commit of the omelette' -a

say 'Pushing to master branch'
gitcmd push origin master

#- To the second tree --------------------------------------------------------

say 'Clone into developer_2'
cd "${TOP}"
gitcmd clone upstream developer_2
cd "${TOP}/developer_2"

say 'Adding bacon'
echo 'Bacon' > bacon
gitcmd add bacon
gitcmd commit -m 'Bacon for the flavor' bacon

say 'Adding an onion'
echo 'Onion, finely chopped' > bacon
gitcmd add bacon
gitcmd commit -m 'Onion because I like it' bacon

say 'Pushing to master branch'
gitcmd push origin master

#- Back to the original tree -------------------------------------------------

say 'Back to tree developer_1'
cd "${TOP}/developer_1"

say 'Update tree developer_1'
gitcmd pull --rebase

say 'Back to the omelette branch'
gitcmd checkout omelette

say 'Changing eggs'
echo 'You need eggs. At least two of them' > eggs
gitcmd commit -m 'Extra eggs' eggs

say 'Back to the master branch'
gitcmd checkout master

say 'Squashing omelettes with extra eggs'
gitcmd merge --squash -s subtree --no-commit -m 'Omelette with extra eggs' omelette

say 'Commit all'
gitcmd commit -m 'The new and improved omelette' -a

say 'Pushing to master branch'
gitcmd push origin master

#- Resync the second tree ----------------------------------------------------

say 'Resyncing developer_2'
cd "${TOP}/developer_2"
gitcmd pull --rebase

Commando "cd" en huidige directory


Geplaatst door hjt op 2013-03-13 14:19:13 | Permanente link | Categorie: ATComputing | Reacties: 0

In mijn blog van de vorige week heb ik beloofd om een technische uitleg te geven van hoe het mechanisme van "huidige directory" precies in elkaar zit.

De "huidige directory" is, technisch gezien, een attribuut van een individueel proces. In de kernel-boekhouding van elk individueel proces kun je opzoeken wat zijn huidige directory is: op welk device, en welke i-node.

Op user-level ken je drie soorten filenamen: beginnen met /, beginnen met ~, en "de anderen". De kernel kent er maar twee: beginnen met / en "de anderen". Padnamen die met een slangetje beginnen moeten door de applicatie eerst worden omgebouwd tot (meestal) beginnend met / voordat ze de kernel in worden gestuurd. Dat geldt voor de shell, maar ook voor andere applicaties die die ~-notatie supporten, zoals vi.

Als de applicatie een padnaam aan de kernel aanbiedt, gaat de kernel op zoek naar de i-node van de laatste component in dat pad. Begint de aangeboden padnaam met een / dan begint de zoektocht in principe bij i-node nummer 2 van het root-filesysteem (maar zie hieronder). Begint de padnaam niet met een slash, dan begint de kernel zijn zoektocht op de plek die het aanbiedende proces als "huidige directory"-definitie in zijn procesboekhouding heeft staan.

Het UNIX procesmechanisme steunt erg op het fenomeen "erven bij geboorte". Ook de definitie van "huidige directory" erft een proces, bij geboorte, van het ouderproces. Sommige processen hebben in hun user-interface een aangrijpingspunt waarmee de gebruiker tegen dat proces kan zeggen: "ga jij eens met de kernel regelen dat hij je omboekt naar directory zus-en-zo". Voor het shell-proces is het "cd"-commando dat user-interface handvat. "cd" is geen losse executable, maar een shell-built-in commando. Het kán technisch geen losse executable zijn. Stel je maar eens voor dat cd wel een losse executable zou zijn: jij commandeert cd met een padnaam, de shell start de cd-executable als kindproces en geeft die padnaam door, het kindproces regelt met de kernel dat hij (kind) in die andere directory wil worden ingeboekt. Daarna is het kindproces klaar en stopt. De ouder-shell komt weer in beweging, en zit nog steeds in de directory waar hij voorheen ook zat (terzijde: over "umask" kun je exact hetzelfde verhaal houden).

Veel grafische programma's hebben een file-chooser box. In twee kolommen zie je links subdirectorynamen, en rechts de namen van "gewone" files. Onder de motorkap heeft dat grafische programma een "ls"-achtige operatie gedaan, en de resultaten gesplitst in: directorynamen links, filenamen rechts. Als je op een filenaam in de rechterkolom klikt dan commandeer je een "file open", maar als je op een naam in de linkerkolom klikt dan gebruik je binnen die grafische applicatie het user-interface handvat voor z'n "cd"-operatie. Dat verklaart ook waarom, als je zo'n applicatie start vanuit een icoontje op je desktop, hij begint in je login-directory. Maar als je diezelfde applicatie start door de programmanaam aan een shell-prompt te typen, dan zie je dat hij begint in de directory die hij van zijn shell-ouder erft.

Van dit "huidige directory" mechanisme is ook het "chroot" mechanisme afgeleid. Hierboven hadden we gezegd: als een proces aan de kernel een padnaam aanbiedt en die naam begint met een slash, dan start de kernel in principe zijn analyse van dat pad bij i-node nr. 2 van het root-filesysteem. Maar net zoals elk proces in zijn kernel-boekhouding zijn eigen definitie van "huidige directory" heeft, heeft elk proces ook zijn eigen definitie van "root directory". Die staat bij default op "i-node 2 van het root-filesysteem", maar dat hoeft niet zo te blijven: je zou een "chroot" kunnen commanderen.

Technisch gesproken is "chroot" echter geen built-in commando van de shell: het is een losse executable. Als je die start als kindproces van je huidige shell doet dat kind achtereenvolgens: chroot("."); chdir("/"); exec..("/bin/bash".... waarbij die "exec" betekent dat het aanvragende proces een "bash" als opvolger op zijn eigen stoel roept.

Daarna gaat de rest zoals hierboven beschreven: de kinderen erven de setting bij geboorte. En als daarna zo'n afstammeling "cd .." commandeert in zijn (!!) root-directory, dan vangt de kernel dat af, want wie al aan zijn plafond zit moet niet nóg verder omhoog willen.

Wat kan er nou foutgaan met een cd-commando


Geplaatst door hjt op 2013-03-01 15:16:54 | Permanente link | Categorie: ATComputing | Reacties: 0

Dat een simpel "cd" commando problemen zou kunnen opleveren geloof je pas als je het meemaakt. Dus toen ik onlangs slachtoffer werd had ik meteen stof voor deze blog.

Al een tijdje geleden ontstond op mijn werkplek een probleem dat een combinatie was van hardware en software. Nadat de hardware was gerepareerd bleef de software problemen houden. Op die werkplek gebruik ik Ubuntu. De package database was vastgelopen, en de automatische updates wilden niet meer. Dat heb ik een flinke tijd zo gelaten (druk, druk, druk en ik zit toch achter een goede firewall :-). Maar uiteindelijk besloot ik om een her-installatie met de nieuwste Ubuntu (12.10) te doen.

Al van mijn pre-Linux dagen ben ik een fan van ksh. Ik schrijf veel scripts, en de bash levert me teveel onaangename verrassingen. Een voorbeeldje:

echo rood wit blauw | read boven midden onder

Dat een bash daarna niet weet welke kleur op $boven, $midden en $onder hoort komt niet door gebrek aan vaderlandsliefde. In een ksh gaat dit (en veel meer) wel zoals je verwacht. In een volgende blog zal ik wat meer vertellen over allerlei verschillen tussen ksh en bash waar ik mee geworsteld heb.

Een ksh komt niet standaard mee in een Ubuntu distributie. Maar gelukkig heeft Canonical 'm wel in het repository. Met "apt-get install ksh" is hij zo binnengehaald. Hij heet dan /bin/ksh93, en /bin/ksh is via-via een werkende symlink.

Na een uurtje gebruiken begon me een raar gedrag op te vallen. Eerst dacht ik dat het aan mijn dikke vingers en mijn vele typefouten lag, maar opeens kreeg ik het door:

cd /tmp; mkdir subdir1 subdir2; cd subdir1; pwd
  antwoord: /tmp/subdir1
cd ../subdir2; pwd
  antwoord: /tmp

Aanvankelijk geloofde ik mijn ogen niet. Dus ik ging variaties uitproberen. Grafische schil weg, ander filesysteem, controleren of symlinks in het pad zitten, dezelfde tests met een bash (die het wel goed deed), "ls" in plaats van "cd" met vergelijkbare padnamen (werkte OK), enz. Ik ging dieper kijken met:

    strace ksh  -c 'cd ../subdir2'
versus:  strace bash -c 'cd ../subdir2'

en daar was duidelijk te zien dat de bash eindigt met een "chdir" system call met de goede (absolute) padnaam, en die vers gedownloade ksh met een foute padnaam. Het bleek echt alleen in het "cd" commando in die ksh te zitten.

Op dat moment sloeg de schrik me om het hart. Veel van mijn scripts beginnen met "#!/bin/ksh" en het kan best zijn dat ergens een "cd ../xyz" gevolgd wordt door een "rm *" Je moet er niet aan denken dat die "rm" in de verkeerde directory wordt uitgevoerd. Een van de afgeleiden van Murphy's Law luidt: "if there is a choice of things to go wrong, the one that will cause the most damage will be the one to go wrong". Dus bij die "rm" mag je nog een extra "-r" flag denken.

Overigens heb ik al heel lang de vaste gewoonte om, als in een van mijn scripts een "rm *" staat, daar altijd een controle op de output-string van een "pwd" voor te programmeren. Maar toch...

De nacht voor de update had ik een kopie van mijn gewone disks naar een grote/goedkope/langzame USB-disk gemaakt. Daar heb ik de "oude" executable van ksh opgezocht, en de tests herhaald. Die deed het uiteraard wel goed; dus ik heb 'm maar over de nieuwe "rotte" executable heengezet.

Ik ben natuurlijk ook gaan Googlen voor deze bug. Veel mensen hebben er last van gehad. Het valt op dat mensen in (te) veel fora beginnen te leuteren over het verschil bij "cd" (en "pwd") met of zonder de "-P" vlag, en dus duidelijk niet weten waar de klepel hangt. Uiteindelijk kwam ik uit bij Debian bug report 67996 en dat vertelt hoe de vork in de steel zit. Iemand heeft aan de ksh zitten prutsen en de zaak verknoeid. Debian heeft alweer een gerepareerde "ksh" (bug rapportage gesloten 8 jan. 2013), maar de Ubuntu/Canonical repository loopt nog achter.

Omdat ik toch een docent blijf zal ik dit verhaaltje volgende week afronden met een technische uitleg over het mechanisme van "huidige directory". Tot dan.

Geluiden uit de keukens van Debian en Fedora (maart)


Geplaatst door martijn_brekhof op 2012-08-07 10:38:23 | Permanente link | Categorie: Systeembeheer | Reacties: 0

Debian

Christoph Anton Mitterer geeft aan dat er de laatste tijd nogal wat gaten zitten in de manier waarop Debian de veiligheid van packages probeert te waarborgen (link). Debian packages zijn ondertekend en de gebruiker kan zo verifiëren dat de software van een betrouwbare bron komt. Maar er zijn ook packages die zelf de software niet bevatten maar tijdens installatie de software downloaden van een website waarbij vervolgens geen controle wordt gedaan of de software wel correct is. Een ander voorbeeld zijn applicaties (zoals Firefox) die zelf software installeren (de zogenaamde extensies). Hier is dan vanuit Debian gezien geen controle op. Het wordt al snel duidelijk dat er grenzen zijn aan wat Debian kan controleren. Maar beheerders van pakketten die software downloaden worden wel verzocht om een checksum toe te voegen aan het pakket. Deze checksum kan dan tijdens installatie worden gebruikt om te controleren of de juiste software is gedownload.

Sergio Cipolla maakt zich druk over de manier waarop een bug omtrent vlc is afgehandeld. Vooral het taalgebruik van Fabian Greffrath stoort hem erg (link). Het blijkt dat de bug niet zozeer te maken heeft met de vlc package in Debian maar met de vlc package in de niet officiële debian-multimedia repository debian-multimedia. Voor de gebruiker is het verschil niet duidelijk en er wordt verder flink gediscussieerd of Debian niet duidelijker moet maken wat de officiele Debian repositories zijn (link). Volgens Stefano Zacchiroli is het gebruik van de naam debian in de URL in strijd met het Debian handelsmerk beleid. Inmiddels is de domeinnaam gewijzigd in deb-multimedia.org.

John Paul Adrian Glaubitz is op zoek naar een manier om gebruikers packages te laten installeren zonder dat ze ook rechten krijgen om packages te verwijderen (link). Hij wil dus dat gebruikers zelf pakketten kunnen installeren, maar wil voorkomen dat ze vervolgens van alles kunnen deïnstalleren. Hij geeft zelf een oplossing via sudo maar vraagt zich af of er een betere manier is. Josselin Mouette geeft aan dat hij aptdaemon hiervoor gebruikt met een patch om ervoor te zorgen dat alleen een beperkte set van packages door gewone gebruikers kan worden geïnstalleerd. Een oplossing die wordt aangedragen is om een lijst op te stellen van packages die door gewone gebruikers mogen worden geïnstalleerd maar dit wordt nog niet ondersteund.

Lars Wirzenius probeert de hevige discussie die de laatste tijd wordt gevoerd omtrent vervanging van het System V init systeem te deëscaleren door een samenvatting te geven (link).

Fedora

Neal Becker vindt dat Linus Torvalds gelijk heeft omtrent het gebruik van root privileges voor simpele taken in Linux-distributies (link). De klacht van Linus Torvalds is dat sommige simpele (beheerders)taken onder Linux root privileges vereisen. Voorbeelden die hij geeft zijn het configureren van netwerktoegang en het toevoegen van een printer. Iedereen is het er over eens dat het huidige model niet voldoet maar een goed alternatief lijkt er vooralsnog niet te zijn. Scott Doty merkt op dat het beleid via policykit goed is en dat voor het toevoegen van een printer normaal niet om een wachtwoord wordt gevraagd als de gebruiker in de wheel groep zit. Door een aantal subtiele bugs in de manier waarop Fedora het systeem implementeert viel het terug op het vragen om het root-wachtwoord (link).

Michał Piotrowski vraagt waarom de configuratie soms in /etc/default/ moet worden gedaan en niet in /etc/sysconfig/ (link). Lars Seipel geeft aan dat dat niet door Fedora bepaald wordt maar door de originele ontwikkelaars van het betreffende softwarepakket. Kay Sievers maakt duidelijk dat /etc/default/ en /etc/sysconfig/ beide niet juist zijn en dat ieder softwarepakket zijn eigen directory onder /etc/ hoort aan te maken voor z'n configuratie. Het is dus de bedoeling dat langzaamaan de directories /etc/default/ en /etc/sysconfig/ verdwijnen.

Geluiden uit de keuken van Debian en Fedora (februari)


Geplaatst door martijn_brekhof op 2012-07-31 11:25:40 | Permanente link | Categorie: Systeembeheer | Reacties: 0

Debian

Ondřej Surý probeert duidelijk te maken waarom hij de Suhosin patch heeft verwijderd uit de PHP-package in unstable (link). Suhosin is een systeem in PHP waarbij bescherming wordt geboden voor bekende en nog onbekende problemen in PHP applicaties. De reden is voornamelijk dat hij geen tijd heeft om de patch te onderhouden en dat hij van mening is dat suhosin niet meer essentieel is aangezien PHP zelf aanzienlijk beter is geworden de laatste tijd. Stefan Esser is het hier totaal niet mee eens en vindt dat Suhosin een belangrijke component is aangezien code altijd bugs zal bevatten. Aangezien er niemand opstaat om het beheer over te nemen ziet het ernaar uit dat Suhosin in de toekomst zal verdwijnen bij Debian.

Ian Jackson geeft aan dat hij van het L10n team te kennen heeft gekregen dat teksten geschreven vanuit de eerste persoon niet gewenst zijn. Volgens hem is het echter volstrekt in overeenstemming met correct Engels. Hij krijgt maar weinig bijval en men vindt het maar raar wanneer een computer naar zichzelf als "ik" refereert. De teksten zullen dus voorlopig nog in de derde persoon moeten worden geschreven. Alhoewel de befaamde README hier dan weer een uitzondering op is (link).

Fedora

Manuel Escudero vraagt zich af of de Ubuntu Unity desktop ooit op Fedora beschikbaar komt (link). Adam Williamson heeft ooit eens geprobeerd Unity te packagen voor Fedora maar moest dit opgeven wegens tijdgebrek. Volgens hem zouden er geen onoverkomelijke problemen moeten zijn om Unity te packagen voor een Fedora systeem. Dus wellicht dat we binnenkort op Fedora ook kunnen kiezen om Unity te gebruiken op de desktop.

Harald Hoyer maakt bekend dat Fedora 17 het basis operating systeem volledig onder /usr zal plaatsen en dat onder /bin, /lib, /sbin, enzovoorts, alleen nog symbolic links zullen staan naar de programma's onder /usr (link).

Matthew Garret vraagt of iemand nog behoefte heeft om het verouderde HFS te beheren. Zo niet dan wil hij graag de hfsplus tools verwijderen uit Fedora (link). Het ziet ernaar uit dat dit inderdaad zal gebeuren.

Allerlei tips en tricks


Geplaatst door tonk op 2012-07-11 16:18:53 | Permanente link | Categorie: Tips and Tricks | Reacties: 0

Tijdens het geven van cursussen worden er door de cursisten vaak veel en soms ook best lastige vragen gesteld. En ik, als docent, weet ook niet op alle vragen direct een antwoord. Maar natuurlijk wil ik het antwoord niet schuldig blijven en de oplossing ook zelf weten en daarom wordt het (meestal 's avonds) alsnog uitgezocht.

Een korte bloemlezing van deze vragen, met de daarbij horende oplossing vind je hier.

Hoe gooi je lege regels aan het begin van een file weg?

Tja, dat was een lastige. Dit kun je natuurlijk doen met een while lus, maar dat is een beetje lomp en zeker niet charmant.

De oplossing die ik uiteindelijk bedacht heb is:

sed '/./,$!d' <filenaam>

Wat er staat is, kort gezegd, "zo gauw je iets gevonden hebt, stop met weggooien".

Als ik een tekststring heb, hoe kan ik dan zien hoeveel spaties er in staan?

Dit probleem is met iedere moderne shell simpel op te lossen:

P="John en Paul en Ringo en George waren 'the Beatles'"
R=${P//[! ]/}       # Verwijder alles uit de string dat GEEN spatie is
echo ${#R}          # Toon het aantal overgebleven tekens (spaties)

Een mooie vraag over het programma awk:

Wanneer de waarde van het tweede veld groter dan 1000 is, hoe druk ik dan de velden 8 tot en met 23 af, zonder dat volledig uit te schrijven?

Dat is nu typisch iets dat vaak in awk voor komt, namelijk een aantal velden afdrukken, zonder dat je alle velden letterlijk noemt. In awk kun je dat oplossen door een variabele te definiëren (zeg i) en dan hiermee het veld laten afdrukken. Bijvoorbeeld het vierde veld afdrukken:

i = 4
print $i

Voor de bovenstaande vraag wordt dit dan zoiets als:

#!/bin/bash

awk '{
    # Als de tweede waarde groter is dan 1000
    if ( $2 > 1000 ) {
        # Voor alle waardes van 8 -> 23
        # Voor alle velden in de regel: i <= NR
        for ( i = 8; i <=  23; i++ ) {
            # Als je een veld wilt afdrukken waarbij de
            # index een variabele is, gebruik dan
            # als veldnaam '$i'
            printf("%s ", $i)
        }
        printf("\n")    # Nog een newline erachter
    }
}' bestand_met_data

En eentje die ik bijna dagelijks wel een keer in een vi sessie intik:

Hoe gooi ik (met vi) alle spaties en tabs aan het einde van alle regels weg?

In vi kun je vanuit commando mode na het tikken van een : allerlei commando's geven om de tekst te manipuleren. Hier komt natuurlijk direct het zoek en vervang commando om de hoek kijken. Zoek naar een of meer spaties of tabs aan het einde van de regel. Als je dit in een reguliere expressie giet, dan wordt dat: [ ][ ]*$, een spatie of een tab gevolgd door nul of meer keren een spatie of een tab, of anders gezegd: een spatie of een tab met eventueel daarachter nog meer "spaties of tabs". De * geeft hier een optionele component aan.

En in vi wordt dit dan:

: 1,$ s/[   ][  ]*$//

Dus ook weer een spatie of een tab met eventueel daarachter nog meer "spaties of tabs". En hierbij kan de 1,$ ook vervangen worden door (het kortere) % teken, wat hetzelfde betekent.

Tijdens de cursus Linux/UNIX deel 1 wordt aan de hand van een handjevol xeyes commando's job control uitgelegd. Deze oogjes volgen je muiscursor en als ze in de status stopped staan, dan volgen de oogjes de cursor niet meer. Wanneer je deze oogjes wilt stoppen dan geven veel cursisten het commando

ps -fu <userid> | grep 'xeyes'

en stoppen dan alle bijbehorende processen. Hierbij komt echter ook altijd het grep proces voorbij en dat is lastig, verwarrend, minder mooi en wat al niet meer. Een cursist had de volgende oplossing:

ps -fu <userid> | grep 'xeyes' | grep -v 'grep'

Werkt wel, maar is niet echt charmant of efficiënt. Veel mooier is

ps -fu <userid> | grep '[x]eyes'

Let hierbij op de reguliere expressie bij het grep commando.

Ik zal dit soort vragen blijven verzamelen en als ik weer een mooie set bij elkaar heb volgt er zeker weer een blog.

Drie handige UNIX en GNU/Linux tips


Geplaatst door maurice op 2012-07-09 14:19:55 | Permanente link | Categorie: Tips and Tricks | Reacties: 0

Drie handige UNIX en GNU/Linux tips

Zoeken en vervangen in Vi

Soms wil je snel even wat woorden in een bestand vervangen door een ander woord. Vi heeft hiervoor een keur aan mogelijkheden om dit voor elkaar te krijgen. De meest gebruikte is eigenlijk domweg het zoeken en vervangen van alle keren dat het woord voorkomt. Dit is wat de meeste andere tekstverwerkingsprogramma's als functie aan boord hebben en dus ook wat de meeste gebruikers verwachten. Om dit uit te voeren ga je de command mode van Vi in door op de esc toets te drukken. Daarna typ je:

:%s/search_string/replacement_string/g

Dat is nogal een volzin. Om deze regel makkelijker te onthouden, helpt het (ten minste, hielp het bij mij ;) ) om te begrijpen wat al die letters en tekens doen. Dit zal ik hier kort uit de doeken doen.

We beginnen met de vorm die je in het midden ziet:

s/ a / b

Die zegt substitute / de letter a / door de letter b. Deze syntax kom je ook tegen in het commando sed en is door Vi overgenomen. Binnen Vi kun je het commando uitvoeren door het volgende te typen binnen de Vi command mode:

:s/a/b

Vi zal nu de eerste match van de letter a op de huidige regel (!) vervangen door b. Het maakt dus niet uit hoeveel keer er een a op de huidige regel staat, alleen de eerste a die Vi tegen komt op de regel waar de cursor staat, wordt vervangen door een b. Het maakt daarbij trouwens ook niet uit waar de cursor staat op de regel. De eerste a vanaf het begin van de regel zal vervangen worden door een b.

Om alle a's te vervangen op de regel waar de cursor staat, gebruik je de g de optie op het einde van het commando:

:s/a/b/g

Nu zullen alle matches met a op de regel waar de cursor staat vervangen worden door een b. De g optie staat (vind ik een beetje misleidend) voor global. Eigenlijk is er weinig global aan de optie, het vervangt alleen alle matches op dezelfde regel waar de cursor staat. Nog niet helemaal wat we willen, maar we zijn er bijna!

De truc om in het hele bestand de a in b te veranderen, is het gebruik van het % teken i.c.m. de g. Hiermee draag je Vi op in een bepaald gebied (range) de vervanging toe te passen.

:%s/a/b/g

Binnen dit commando betekent het % teken "alle regels". Het is een korte manier van schrijven van 1,$. Normaal betekent $ het einde van de regel binnen Vi, maar hier staat het dollarteken voor de laatste regel van de file. Je kan in plaats van het % teken ook andere gebieden (ranges) opgeven. Bijvoorbeeld een punt . voor de huidige regel, of 12,15 wat ervoor zorgt dat de substitutie alleen gebeurt tussen (en inclusief) regel 12 en 15 van het bestand. Dus als samenvatting geef ik hier de volledige syntax:

:gebied s/patroon/string/[c|g|i|I]

Wil je meer weten over dit commando? Kijk dan hier

Voer laatste commando uit als root gebruiker

Soms komt het voor dat je vergeet om root-rechten te verwerven voordat je een root commando intypt in een shell. Zeker bij Debian-achtige Linux- distributies (lees Ubuntu) kun je hier nog weleens last van hebben. Mocht je net een kleine roman aan commando en vlaggetjes op de shell regel getypt hebben, dan is dat erg frusterend. Een snelle oplossing is deze:

sudo !!

Daarmee voer je de laatst ingetypte commando regel uit als de root-gebruiker. Deze commandoregel is een combinatie van het commando sudo met de shell-builtin functie ! (het uitroepteken wordt ook wel bang genoemd in het Engels). Omdat het ! een shell built-in functie is werkt deze niet in alle shells. Met het uitroepteken kun je meer combinaties maken. Alle combinaties grijpen terug op de al ingetypte commando's op de commandoregel. Deze zogenaamde bang-commands kunnen je veel tikwerk besparen op de commandoregel. Voorbeelden hiervan en meer uitleg (in het Engels) vind je hier hier

Twee tekstbestanden vergelijken

De programmeurs onder ons zullen vast bekend zijn met het commando diff. Hiermee kun je makkelijk twee source code bestanden vergelijken en de verschillen weergeven. Voor normale tekstbestanden, werkt het programma diff echter minder handig. Een alternatief vond ik een paar jaar geleden. Dit is wdiff en staat voor word-diff. Dit programma doet exact wat je verwacht. Het laat je de verschillen op woordniveau zien tussen twee tekstbestanden. Het vlaggetje -3 van dit commando laat daarnaast alle overeenkomstige tekst tussen de twee bestanden weg. Hierdoor zie je dus heel snel welke woorden (stukken) er veranderd zijn. Hier vind je de handleiding van wdiff.

Geluiden uit de keuken van Debian (januari)


Geplaatst door martijn_brekhof op 2012-06-14 15:05:44 | Permanente link | Categorie: Systeembeheer | Reacties: 0

Doordat het de afgelopen maanden erg druk is geweest, door met name de ontwikkeling van onze nieuwe cursus Android Development Fundamentals, is het bloggen er een beetje bij ingeschoten. Ik zal de komende tijd proberen de schade te herstellen. Hier dan de geluiden uit de keuken van Debian in januari. Het was natuurlijk niet stil bij Fedora maar er was niets dat ik noemenswaardig vind om aan jullie te melden.

Bij Debian wordt er flink gediscussieerd over het nut van het samenvoegen van / en /usr. Zachary Harris vindt dat Debian zich moet houden aan de Filesystem Hierarchy Standard en dat afwijkingen hiervan alleen in afgeleiden van Debian mogen (link). Roger Leigh maakt duidelijk dat / en /usr in een distributie bij elkaar horen en dat het argument van gescheiden houden voor NFS exports onzin is. Hij vraagt zich dan ook af wat nog een echte reden is om /usr apart te houden (link). Eén reden die wordt aangedragen is snelheid wanneer je gevoelige data op / versleutelt. Je hoeft dan alles onder /usr niet te versleutelen (link). Volgens Roger Leigh gaat het dan echter niet om / in z'n geheel maar alleen om /etc en hij vindt dit geen reden om tegen de samenvoeging van / en /usr te zijn.

Tanguy Ortolo vindt dat de ondersteuning onder UEFI wat meer vaart mag krijgen en legt uit wat er volgens hem nog moet gebeuren (link). Kort gezegd moet er een UEFI-bootable installatie medium komen, partman partioneren moet aangepast worden, en de Debian installer moet een UEFI bootloader installeren.

Thomas Goirand vraagt zich af of het noodzakelijk is dat de PHP-pakketten een afhankelijkheid hebben op de Apache webserver. Dit maakt het voor hem lastig om vele websites in een chroot omgeving te beheren. Wanneer hij namelijk PHP in de chroot omgeving installeert wordt ook Apache daar geïnstalleerd (link). Hij krijgt als advies om equivs te gebruiken waarbij je een leeg pakket aanmaakt dat bijvoorbeeld Apache levert. Het package-systeem denkt dan dat Apache al is geïnstalleerd wanneer de PHP-pakketten worden geïnstalleerd. Raphael Geissert maakt duidelijk dat PHP pakketten die afhankelijk zijn van Apache eigenlijk fout zijn. PHP code is niet afhankelijk van een webserver maar van een interpreter (link).

Ansgar Burchardt stelt voor om een pseudo package in het Debian Bug Tracking systeem te gebruiken om verzoeken voor sponsoring van pakketten af te handelen (link). Hier wordt erg positief op gereageerd en dit is inmiddels geïmplementeerd. Het voordeel van de nieuwe methode is dat verzoeken beter te volgen zijn en minder snel worden vergeten wanneer er erg veel verzoeken tegelijkertijd binnenkomen.

Scp/rsync speed up


Geplaatst door jacco op 2012-05-02 20:16:32 | Permanente link | Categorie: Tips and Tricks | Reacties: 0

Zo, eventjes een dikke file naar de andere kant pompen ...

$ scp dikkefile pietje@anderemachine:
dikkefile           100% 1024MB  37.7MB/s   00:27

Mmmh, 37 schamele megabytes per seconde. Het is dat het meer is dan 10 MB/sec, anders zou je nog denken dat je per ongeluk data over een verbinding aan het trekken bent waar een fast ethernet component de zwakste schakel is.
De brondisk en de doeldisk zijn snel (> 100MB/sec), er ligt gigabit ethernet tussen en beide machines zijn op dezelfde ethernetswitch aangesloten. Kan dat niet sneller?
Jazeker kan dat! En daar hoef je maar weinig voor te doen.

Wanneer je regelmatig grote hoeveelheden data via scp of rsync naar een andere machine verstuurt, loont het de moeite om aan de default instellingen van ssh te sleutelen. Om het verkeer te versleutelen gebruikt ssh een bepaalde cipher (versleutelingsmethode). En de default cipher blijkt niet uit te blinken in snelheid.
Als je in de manpage van ssh_config zoekt naar "Ciphers", wordt een hele lijst versleutelingsmethodes opgehoest. De ssh-client gaat in conclaaf met de ssh-server om een te gebruiken cipher af te spreken. Volgens de manpage begint de lijst met aes128-ctr en eindigt hij met arcfour, met daar tussenin nog 10 andere ciphers. Tenminste, bij de OpenSSH die Ubuntu gebruikt. De eerste uit de lijst die zowel client als server ondersteunen, wordt als cipher gepakt.

Het is maar een kleine moeite om vanaf de commandline een andere cipher aan te prikken, namelijk met de vlag "-c".
Mocht je per ongeluk een cipher pakken waarvan de server "ken ik niet" zegt, zie je dat gelijk. In onderstaand voorbeeld is de client een Ubuntu 10.04 machine en de server een OpenIndiana (SunOS 5.11) machine:

$ scp -c blowfish-cbc dikkefile pietje@anderemachine:
no matching cipher found: client blowfish-cbc server aes128-ctr,aes192-ctr,aes256-ctr,arcfour128,arcfour256,arcfour

Afhankelijk van de gekozen cipher, kan het ook nuttig zijn compressie te gebruiken (vlaggetje "-C").
Ik heb een handvol beschikbare ciphers naast elkaar gezet, om de performanceverschillen inzichtelijk te maken. Daarbij heb ik uitgesloten dat de disken een bottleneck zouden vormen, door vanaf een ram-filesystem te lezen en op de doelmachine ook naar een ram-filesystem te schrijven.

Cipher Snelheid (MB/sec)
default 37.9 (cpu-utilization: 10% sys, 90% user)
default met compressie 51.2 (cpu-utilization: 9% sys, 91% user)
3des 25.6
3des-cbc 23.8
blowfish/blowfish-cbc 53.9
aes128-cbc (cipher block chaining) 60.2
aes128-ctr (counter mode) 37.9
aes256-cbc 53.9
arcfour 78.8 (cpu-utilization: 33% sys, 66% user)
arcfour met compressie 51.2 (cpu-utilization: 9% sys, 91% user)

Het blijkt dat de cipher "arcfour" er - qua snelheid - met kop en schouders bovenuit steekt.

Wanneer je deze als jouw persoonlijke favoriet instelt (keyword "Ciphers" in de file ~/.ssh/config), maakt alle tooling die gebruik maakt van ssh van deze cipher gebruik. Dat geldt o.a. voor scp en rsync.
Er is overigens wel een reden dat arcfour (oftewel "Alleged RC4") niet de default is. Het is - vanuit security-oogpunt - niet de meest veilige versleuteling. Maar daar was het me ook niet om te doen: bij flinke kopieeracties wil ik die gigabit pijp gewoon dichttrekken, als het even kan.

Meer leesvoer over RC4 kun je vinden op wikipedia.

Nota Bene: Wanneer je over een verbinding met een hoge latency een niet optimale snelheid haalt, kan dat ook een andere oorzaken hebben. Kijk dan eens naar de TCP-window size op de ontvangende en zendende machine. Het probleem kan dan zitten in de beperkte bufferruimte voor TCP-pakketjes (een machine kan maar een beperkt aantal pakketjes opsturen voor hij een ontvangstbevestiging van de overkant vereist).

Performance: If statements elimineren


Geplaatst door martijn_brekhof op 2012-01-13 11:35:21 | Permanente link | Categorie: Programmeren | Reacties: 0

Ik was in de blog Performance: lazy evaluation begonnen met het optimaliseren van onderstaand loopje.

for ( bug_number = 0; bug_number < MAX_BUGS; bug_number++ )
{
  for ( employee_id = 0; employee_id < MAX_EMPLOYEES; employee_id++ )
  {
    if( ( employee_employed[employee_id] == false ) && (employee_id == bug_assignee[bug_number]) )
    {
      printf("Bug %d assigned to non employed person\n", bug_number);
    }
  }
}

Door simpelweg de condities in de if-statement om te draaien behaalde ik een winst van 76%. In de blog Loopjes, loopjes, loopjes ging ik verder in op bovenstaand stukje code waar ik vooral de for loop onder de loep nam. Door simpelweg de binnenste loop alleen uit te voeren als het een medewerker betreft die niet meer in dienst is, behaalde ik een extra winst van 1 seconde. De uiteindelijke code laat ik hier voor het gemak nog even zien:

for ( employee_id = 0; employee_id < MAX_EMPLOYEES; employee_id++ )
{
  if (employee_employed[employee_id] == false)
  {
    for ( bug_number = 0; bug_number < MAX_BUGS; bug_number++ )
    {
      if (employee_id == bug_assignee[bug_number])
      {
        printf("Bug %d assigned to non employed person\n", bug_number);
      }
    }
  }
}

Ik zal nu de (voor mij) laatste slag in de optimalisatie van de code behandelen. Door het herschrijven van de code viel me namelijk iets anders op. Wellicht dat het je al was opgevallen, maar de twee if-statements gebruiken allebei de variabele employee_id.
De lijst bug_assignee geeft een employee_id terug en de lijst employee_employed geeft op basis van employee_id aan of deze nog in dienst is. Ik had in de blog Performance: lazy evaluation al aangegeven dat een bug altijd toegewezen is aan één persoon. Oftewel bug_assignee geeft altijd een valide medewerkersnummer terug (employee_id). We hoeven hier dus helemaal niet expliciet op te controleren en kunnen de twee for loops herschrijven tot één:

for ( bug_number = 0; bug_number < MAX_BUGS; bug_number++ )
{
  employee_id = bug_assignee[bug_number];
  if( employee_employed[employee_id] == false )
  {
    printf("Bug %d assigned to non employed person\n", bug_number);
  }
}

De code is nu een stuk leesbaarder (vind ik in ieder geval) en doet er nog maar 0.3 seconden over. Dit is dus uiteindelijk een winst van 99% ten opzichte van de code in mijn blog Performance: lazy evaluation.