Emil Vikström

Emil Vikström

Datorer och webben

Emil Vikström RSS Feed
 
 
 
 

Anropa kommandoraden från PHP

Foto: kitroed

Foto: kitroed @ flickr

PHP innehåller redan väldigt mycket färdiga funktioner som underlättar vardagen. Men om du tillhör min generation (80-talisterna) så vill du säkert bara ha mer ändå. Som tur är kan PHP ge dig mer! Det finns flera sätt att anropa vanliga kommandoradsprogram direkt från din PHP-kod. Resultatet kan bli allt från automatisk bearbetning av PDF-filer till en omstart av hela din server via en knapp på en webbsida. Här går jag igenom hur du gör anropen. Hur du ska använda det får du klura ut själv, eller fortsätta följa min webbsajt eftersom jag kommer gå igenom många fiffiga kommandoradsprogram framöver.

Backtick-operatorn

Det lättaste sättet är att använda backtick-operatorn. Den används, på ett svenskt QWERTY-tangentbord, genom att man håller ner Skift och trycker på knappen till vänster om suddaknappen, för att sedan släppa båda knapparna och trycka på mellanslag. Resultatet blir detta vackra tecken: `

Du använder den på detta sätt:

$resultat = `echo Hej`;
// Variabeln $resultat innehåller nu ordet Hej

Backtick-operatorn kör kommandoradsprogrammet, väntar på att det körts klart och returnerar sedan hela resultatet (stdout).

Exakt samma resultat får du av funktionen shell_exec. Exempel:

$resultat = shell_exec('echo Hej');

exec()

Funktionen exec är mer flexibel än backtick-operatorn. Den kör programmet på samma sätt, men du kan välja mer exakt vad du vill få tillbaka. Funktionen returnerar bara den sista raden av stdout.

$resultat = exec('cat /proc/meminfo');
// $resultat innehåller: Hugepagesize:     4096 kB

Kommandot tar även ett andra och ett tredje argument om man vill. Dessa ska då vara variabler. Det andra argumentet kommer efter körningen att vara en array som innehåller ett element för varje rad. Det tredje argumentet kommer efter körningen att innehålla en siffra som motsvarar programmets statuskod (siffran noll betyder att kommandot avslutades korrekt, andra tal är olika sorters felkoder).

$resultat exec('cat /proc/meminfo', $rader, $slutstatus);
// $resultat innehåller samma som ovan. $rader blir en array. $slutstatus innehåller statuskoden.

system()

Funktionen system liknar exec litegrann i det att den returnerar sista raden, men den har en viktig skillnad: hela resultatet av programmet skickas till webbläsaren! Varje rad för sig skickas alltså ut till den webbläsare som startat skriptet.

Ett andra argument kan skickas med om man vill, och ska då vara en variabel. denna variabel kommer att innehålla slutstatusen för programmet, på samma sätt som tredje argumentet till exec.

$resultat = system('cat /proc/meminfo', $statuskod);
// $resultat: Hugepagesize:     4096 kB
// $statuskod: Huruvida programmet avslutades korrekt eller stötte på något fel
// Din webbläsare: Hela innehållet från filen /proc/meminfo

fpassthru()

fpassthru funkar nästan likadant som system. En skillnad är att varje enskild byte skickas rakt av till webbläsaren (medan system köar upp och skickar en rad åt gången). Den andra skillnaden är att funktionen inte returnerar någonting.

$resultat = fpassthru('cat /proc/meminfo', $statuskod);
// $resultat: tom
// $statuskod: Huruvida programmet avslutades korrekt eller stötte på något fel
// Din webbläsare: Hela innehållet från filen /proc/meminfo

fpassthru bör användas speciellt om det är binära data, till exempel bilder, som ska skickas till webbläsaren.

popen()

Kanske börjar du nu bli sugen på lite mer avancerade anrop? Hur gör man till exempel om man vill kunna prata med programmet man startat (genom programmets stdin)? popen kommer till din räddning! popen fungerar ungefär som fopen, fast den startar ett program istället för att öppna en fil. Resultatet blir ett filhandtag som du kan använda med funktionerna fwrite, fgets och fgetss (och övriga funktioner för strömmar).

$h = popen('wget -i -', 'w'); // andra argumentet är w eller r
fwrite($h, 'http://google.com'."\n");
fwrite($h, 'http://yahoo.com'."\n");
pclose($h);

Ovanstående kod kommer starta wget, som används för att ladda ner filer på kommandoraden, och be wget att ladda ner Googles och Yahoos förstasidor. Observera radbrytningen \n.

Märk väl att popen bara kan öppnas i ett läge åt gången, antingen kan du skriva till programmet (w) eller läsa svaret (r). Om du läser blir resultatet i princip samma som att köra någon av de tidigare funktionerna, fast du läser med fgets eller fgetss och får chans att bestämma hur mycket du ska läsa åt gången.

Läsa och skriva på samma gång med proc_open

Om du vill kunna både läsa och skriva till ett program så börjar det bli trassligt på riktigt. Funktionen proc_open klarar av detta då den, likt popen, öppnar programmet och ger dig ett par filhandtag som du kan läsa från och skriva till. Jag tänker inte förklara funktionen i detalj, utan du får läsa manualsidan (som innehåller bra exempel!) och använda dig av de kunskaper du fått av min artikel.

Här bygger jag vidare på wget-exemplet genom att jag låter wget returnera sitt svar till stdout och sedan läsa in detta till en variabel:

$spec = array(
0 => array("pipe", "r"),  // stdin i programmet
1 => array("pipe", "w")  // stdout, från programmet till min kod
);

// Starta wget
$h = proc_open('wget -i - -O -', $spec, $pip);

//Ge det URL:erna att ladda ner
fwrite($pip[0], 'http://google.com'."\n");
fwrite($pip[0], 'http://yahoo.com'."\n");
fclose($pip[0]);

//Läs in resultatet
$resultat = '';
while(!feof($pip[1])) {
$resultat .= fgets($pip[1]);
}

fclose($pip[1]);

//Avsluta wget
$statuskod = proc_close($h);

Säkerhet - en kort genomgång

Viktigt att tänka på här är säkerheten. Programmen du startar i dina skript kommer att köras med samma behörigheter som din webbserver, troligtvis alltså med skrivrättigheter till vissa kataloger. Ett litet snedsteg från din sida kan göra det möjligt för en hackare att stänga av Apache (eller vad du nu kör för webbserver) eftersom webbservern ofta har rätt att stänga av sig själv.

Om du ska skicka in variabler istället för hårdkodade strängar i någon av funktionerna jag har beskrivit här så måste du vara mycket noga med att besökaren inte själv kan lägga in oväntade värden i variablerna. Använda dig av funktionerna escapeshellcmd och escapeshellarg för att skydda dig mot farliga värden.

Observera att många av dessa funktioner inte fungerar, eller får ändrade egenskaper, om safe_mode är aktiverat. Safe_mode-inställningen är numera utrotningshotad från PHP och försvinner i PHP 6 så detta bör inte vara något att oroa sig alltför mycket för, men läs manualen om du stöter på problem.

Tillämpa dina nya kunskaper!

Försök komma på roliga småprojekt nu där du kan tillämpa dina nya kunskaper om PHP och körning av program. Läs manualsidorna för lite roliga kommandon. Några tips:

  • pdftk
  • pdftotext
  • imagemagick och convert
  • file (på kommandoraden alltså, inte i PHP)
  • find
  • wget
  • ping
  • mplayer eller vlc (en webbradio kanske?)
  • xmessage (om du kör webbservern på din arbetsstation och behöver visa meddelanden?)
  • top
  • ssh

Observera att jag här skrivit om PHP som om det bara kan användas för webbsidor. Du kan köra PHP helt fristående på kommandoraden också, utan webbserver eller webbläsare, och då kan du få ännu större nytta av tipsen i den här artikeln. Men detta är inget jag tänker gå igenom nu.

Diskutera