Tema: Programmering; sjanger: Meninger
Skrevet av Andreas Nordal den 24. februar 2011 kl 13:31:25; Kommentarer: 0
Hvorfor monger ssh-klienten når den startes fra PHP? Ikke gjør dette hjemme:
#!/usr/bin/php
<?php
system('ssh meg@domene');
?>
Det som tilsynelatende skjer da er at ssh bufrer unna all interaktivitet. Du får ikke se hva du skriver i kommandolinja før du har trykka enter, men da er det jo for seint. Og skal du redigere tekst over ssh? Da er du kjørt...
Slik skal det gjøres:
#!/usr/bin/python import subprocess subprocess.call(['ssh', 'meg@domene'])
Tema: Programmering; sjanger: Artikler
Skrevet av Andreas Nordal den 11. april 2010 kl 21:22:18; Kommentarer: 0
For de som ikke kjenner goto: Goto forteller datamaskinen at den skal fortsette et annet sted i programmet. Slik vi kjenner det i programmeringsspråket C, er goto en videreføring av jump-instruksjonen, fra den tiden man programmerte i assembly (eller direkte maskinkode for den saks skyld), og er dermed blant de eldste programmeringskonseptene. En vanlig oppfatning er at goto er utdatert. Det var faktisk en hovedhensikt med såkalt strukturert programmering, et konsept i høynivå-programmeringsspråk, å eliminere behovet for å bruke goto.
Kritikere hevder at bruk av goto fører til «spaghettikode», det vil si kildekode som er vanskelig å følge fra start til slutt, for å ikke nevne baklengs, man ser jo ikke hvor man "kommer fra". Dette avhenger selvsagt av hva man gjør det til, så hvis man klarer å bruke goto på en ryddig og konsekvent måte, så er det kanskje tilgivelig.
Tilgivelig eller ikke, noen ganger er det veldig praktisk å bruke goto. Ja, jeg vil gå så langt som å hevde at det kan være fornuftig! Dette har jeg prøvd å vise med kodeeksempelet nedenfor, men først en hverdagslig analogi: Forestill deg at du følger en oppskrift som består av mange steg. Hvert steg kan gå galt, og hvis noe går galt, må du avblåse hele prosjektet og rydde opp. Mengden opprydding vil avhenge av hvor langt du kom før du avbrøt. Selvsagt står ikke oppryddingsprosedyrene i oppskriften for hvert steg som kan gå galt; de er irrelevante for selve algoritmen. Det er i algoritmen utfordringa ligger, mens feilhåndtering på en måte sier seg selv og har ingen ting å si for om resultatet blir riktig i det normale tilfellet. Så hvordan kan vi separere algoritme og feilhåndtering? En måte er å håndtere suksesstilfellet der og da som et slags spesialtilfelle. Suksess på suksess vil da i kildekode få en sinnssyk indentering. Det er ikke uten grunn at programmeringsspråk som C++ og Java har "exceptions", en alternativ kontrollflyt for å avbryte, som er til for å la feilhåndteringa skje et annet sted. Det mer primitive programmeringsspråket C har ikke exceptions, men så har vi jo det gamle velkjente skitne trikset.
De to hypotetiske programsnuttene nedenfor viser feilhåndtering henholdsvis med if/else og if+goto. De gjør nøyaktig det samme, nemlig å initialisere «struct karamell»:
//Denne delen hører med til begge eksemplene. #include <stdlib.h> #include <stdio.h> #include <string.h> struct karamell{ char * navn; void * buf; // : // · FILE * fp; };
//Eksempel 1: if/else struct karamell * k_init(char * filnavn){ struct karamell * k; k = malloc(sizeof(*k)); if(k == NULL){ perror("malloc"); }else{ //OK k->fp = fopen(filnavn, "rb"); if(k->fp == NULL){ perror(filnavn); }else{ //OK k->navn = strdup(filnavn); if(k->navn == NULL){ perror("strdup"); }else{ //OK // . // . // . // ......... // . // . // . // ......... // . if(posix_memalign(&(k->buf), 1024, 1024) != 0){ fputs("posix_memalign failed.", stderr); }else{ //OK /* Nothing bad happened, yet * we are in deep indentation! * We are treating the normal * case as the special case. * Here is where we do our work: * : * · */ return k; } free(k->navn); //cleanup // . // . // . // . } fclose(k->fp); //cleanup } free(k); //cleanup } return NULL; }
//Eksempel 2: if+goto struct karamell * k_init(char * filnavn){ struct karamell * k; k = malloc(sizeof(*k)); if(k == NULL){ perror("malloc"); goto die; } //OK k->fp = fopen(filnavn, "rb"); if(k->fp == NULL){ perror(filnavn); goto fopenfail; } //OK k->navn = strdup(filnavn); if(k->navn == NULL){ perror("strdup"); goto noname; } //OK // : // · if(posix_memalign(&(k->buf), 1024, 1024) != 0){ fputs("posix_memalign failed.", stderr); goto nobuf; } //OK /* Nothing bad happened -> no extra indentation. * The normal case is no special case! * Here we do our work: * : * · */ return k; //exceptional cleanup nobuf: // : // · free(k->navn); noname: fclose(k->fp); fopenfail: free(k); die: return NULL; }
Hva er problemet med det første kodeeksempelet?
Den observante leseren vil legge merke til at disse kodesnuttene er så like at de vil kompilere ned til de samme instruksjonene. Jeg sjekka den gcc-genererte assembly-koden, og fant to ubetydelige forskjeller (en NOP og en ubrukt etikett i goto-versjonen).
Til slutt, hvis du tror jeg er den eneste som bruker goto på denne måten, og at ingen ville finne på å bruke goto like flittig som meg, ta et googlesøk på "linux/fs/ext2/dir.h", så skal du få deg en bakoversveis!