Thursday, March 20, 2014

Waarom PowerShell beter is dan sh, bash, ksh en csh.

Heel, heel lang geleden hadden computers nog geen eenvoudige manier om commando’s in te geven. Eind jaren zestig, begin jaren zeventig doet de ‘Shell’ zijn intrede. Een shell is een manier om commando’s in te geven buiten de kernel. Veel van de huidige computergebruikers denken dan aan MS-DOS maar de shell heeft daarvoor al een ruime historie in de wereld van Unix en zijn voorlopers.

In 2003 kondigde Microsoft een nieuwe shell aan, eerst heette die nog Monad en pas later hebben we kennis gemaakt met PowerShell. Inmiddels zijn commando’s als netstat.exe, ipconfig.exe en dnscmd.exe achterhaald en vervangen door cmdlets als Get-NetTCPConnections, Get-NetIPAddress en Add-DnsServerResourceRecordCName.

PowerShell commando’s hebben een consistente opbouw en beginnen altijd met een werkwoord (*) gevolgd door een zelfstandig naamwoord. Met Get-Mailbox vraag je informatie op, met Set-Mailbox stel je iets in en Remove-Mailbox verwijder je de mailbox. Gaat het om een certificaat, gebruiker, registry key, directory, een ACL of wat voor ander object dan ook? De opbouw van de cmdlets is altijd gelijk. Vergelijk dat eens met Unix commando’s als fc, man, pwd en bg.

Maar wat nog veel belangrijker is, met PowerShell werken we met objecten en Unix werkt met tekst. Het volgende voorbeeld gebruik ik graag om het verschil uit te leggen. Stel dat we het aantal bestanden willen tellen in een directory. Met Unix kunnen we dat doen door de uitvoer van ls (list) met de l optie (lijst met informatie) door te voeren aan wc (word count) met de l optie (lines). Het commando ziet er dan zo uit:

ls –l | wc –l

image

De uitkomst is 5, toch best verrassend aangezien er maar vier bestanden staan. De verklaring is dat de output van ls –l bestaat uit vijf regels tekst, de bovenste regel geeft namelijk het totale aantal ‘blocks’ weer. Voor het echte antwoord moeten we er dus eigenlijk 1 afhalen.

Met Unix en ook Linux werken we dus met tekst. Het ene commando geeft tekst als output, met een volgend commando kunnen we deze tekst verder verwerken. Als we data uit de tekst willen halen dan moeten we die parsen met tools als awk of sed waarmee we dus de regel ‘totaal 0’ uit het resultaat hadden kunnen filteren.

In PowerShell doen we nu hetzelfde. We vragen de items op in de huidige directory met Get-ChildItem (ik had ook één van de aliassen als dir of ls kunnen gebruiken) en de uitvoer van dit cmdlet voeren we door aan Measure-Object.

Get-ChildItem | Measure-Object

image

De uitkomst is hier 4, het correcte antwoord dus. De reden hiervan is dat PowerShell werkt met objecten, niet met ‘domme’ tekst. Weliswaar wordt de output van Get-ChildItem op het scherm weergegeven op vergelijkbare wijze als het DOS commando dir of ls in Unix maar dat is domweg de standaard output naar scherm. Als ik de output doorvoer naar Measure-Object dan zien we dat deze niet de 10 of 11 regels tekst meet maar de vier objecten die Get-ChildItem gevonden heeft in de huidige directory.

Met deze eenvoudige demo hebben we dus het verschil tussen tekst en objecten gezien. Shells als MS-DOS, Unix en Linux werken met tekst die je kunt parsen om er iets zinvols mee te doen. PowerShell werkt met objecten waar je direct slimme dingen mee kunt doen.

(*) Beginnen PowerShell cmdlets altijd met een werkwoord?

4 comments:

Martijn Moret said...

Je moet wel appels met appels vergelijken, ls -l is niet hetzelfde als ls -1 (cijfer één). Dit zorgt al jaren voor hetzelfde resultaat.

Jetze Mellema said...

Het doel van deze demo is het verschil tussen tekst en objecten te laten zien, het voorbeeldje met ls -l is gewoon heel aansprekend om hiervoor te gebruiken. Netstat was een prima alternatief geweest. Het punt is dus niet dat PowerShell of bash nu beter is in het tellen van bestanden. :)

Jeff Wouters said...

Leuke post :-)
Als het op tellen aankomt:
(ls).count

Jetze Mellema said...

Inderdaad Jeff, die gebruik ik in de praktijk heel vaak. Vraag bijvoorbeeld alle mailbox moves op met een filter op de status, haakjes er om heen en .count er achter en je hebt het aantal. Ontdekte Measure-Object toevallig pas een keer. :)