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
gitgoed geconfigureerd is:git config --global user.name 'Ton Kersten' git config --global user.email 'Ton.Kersten@ATComputing.nl'Kopieer het gewenste project op
githubnaar 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/ansibleIn de meeste gevallen is het wel aan te raden om eerst een nieuwe tak te starten, omdat je in
githubslechts 1 patch-aanvraag per tak mag hebben.git checkout -b nieuwetakNu 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
githubrepository. Maar eerst moet je alle tussentijdse commits samenvoegen tot een commitgit checkout master git merge --squash -s subtree --no-commit -m 'De ultieme patch' nieuwetaken bevestig deze samengevoegde set
git commit -m 'De ultieme patch' -aen 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
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
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.
