Parancssori programozás PHP-vel

A PHP weben használva egy nagyon hasznos találmány, parancssorban használva pedig mindennapi életünket könnyítheti meg. A PHP-t ráadásul felkészítették ilyen felhasználásra is, tovább segítve a programozót.

A lehetőségek bemutatását egy kódon keresztül, elsődlegesen Windows platformon mutatom be, Linux alatt kicsit egyszerűbb is az életünk. Ugyanis hogy igazán jó eszköz legyen a kezünkben, egy kicsit bele kell nyúlnunk a Windowsunkban. Hogy megértsük, miért is szükséges a következő pár lépés, menjünk először a Start menü Futtatás elemére, írjuk be, cmd és üssünk entert. Megnyílt a parancssor, amit a C:\Windows\System32 mappában van. Ezt az elérési utat a Windows a Path nevű ún. Környezeti változóból vette, ezt fogjuk szerkeszteni, hogy a PHP-t is megtalálja.

Ha már itt vagyunk, írjuk be a parancssorba, hogy Path, majd üssünk egy entert. Figyeljük meg, hogy minden elem ; jelekkel van elválaszva, és minden útvonal C:\-val kezdődik (nem baj, ha mégsem, ez a lényeg)!

Mielőtt tovább megyünk, egy fontos dolog: legtöbbünk gépén a WAMP szentháromság egy programcsomagként került fel (Xampp, EasyPHP, …), ami a webes fejlesztésekhez tökéletes, CLI-hez hackelni kéne (valószínűleg más php.ini-t használnánk a két környezetben), úgyhogy töltsük le a legújabb Windows futtatható állományokat a php.net-ről, majd tömörítsük ki egy mappába (a telepítő állományt is használhatjuk, csak jegyezzük fel, hova telepítettük). (A következőkben feltételezem, hogy a C:\Program Files\PHP 5 könyvtárba telepítettünk)

A telepítés/kitömörítés végeztével kattintsunk a Sajátgépen (My Computer) jobb gombbal, menjünk a Tulajdonságok (Properties) elemre. A megnyíló ablakban válasszuk a Speciális (Advanced) fület, majd a Környezeti Változók (Environment Variables) gombra kattintva megnyíló ablakban keressük meg a Path változót, duplaklikkeljünk rajta, majd a végére illesszük be a PHP-n elérési útvonalát! Ha a rendszermeghajtónkra (C:) telepítettük, akkor a meghajtó betűjele helyett használjuk a %systemdrive% helyettesítő karakterfüzért! Kattintsunk az OK gombra, és zárjuk be a Rendszertulajdonságok (System Properties) ablakot! A fenti módon nyissunk egy Parancssort, és ellenőrizzük, hogy a Path-ben szerepel-e a PHP elérési útja!

Egyszerűen társíthattuk volna a php.exe-hez a .php fájlokat, viszont így bárhonnan egyszerűen hívhatóak parancssorból, és továbbra a kedvenc szerkesztőnk nyílik meg egy php fájl kiválasztására. A leírás elején írtam, hogy Linuxszal egyszerűbb a dolgunk: egyszerűen beírjuk a fájl első sorába a PHP értelmező elérési útját, innentől kezdve a fájlunk, mint bármilyen shell script, futtatható. Például:

1
2
3
4
#!/usr/bin/php
<?php
print file_get_contents("hello.txt");
?>

És hogy ez mire használható? Nézzük a következő kódot:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#!/usr/bin/php
<?php
$filename = array('Folder.jpg', 'Cover.jpg');
$outfile = 'Folder.jpg';
if(!(IsSet($stdout) && is_resource($stdout))) {
$stdout = fopen('php://stdout', 'w');
}
function find($path, $filename) {
$dir = opendir($path);
while($object = readdir($dir)) {
if($object == '.' || $object == '..') continue;
if(Is_dir($path  . '/' . $object)) {
find($path  . '/' . $object, $filename);
}
if(Is_file($path  . '/' . $object) && ((Is_string($filename) && $object == $filename) || Is_array($filename) &&; in_array($object, $filename))) {
$GLOBALS['files'][] = $path . '/' . $object;
}
}
}
find('.', $filename);
$min = 10000;
foreach($GLOBALS['files'] as $file) {
$size = getimagesize($file);
if($size[0] &lt; $min) {
$min = $size[0];
}
}
foreach($GLOBALS['files'] as $file) {
$orig = imagecreatefromstring(file_get_contents($file));
$new = imagecreatetruecolor($min, imagesy($orig) * ($min / imagesx($orig)));
imagecopyresampled($new, $orig, 0, 0, 0, 0, imagesx($new), imagesy($new), imagesx($orig), imagesy($orig));
imagejpeg($new, dirname($file) . '/' . $outfile, 100);
fwrite($stdout, $file . " resized to " . dirname($file) . '/' . $outfile . " (" . imagesx($orig) . "x" . imagesy($orig) . " to " . imagesx($new) . "x" . imagesy($new) . ")\r\n");
imagedestroy($new);
imagedestroy($orig);
}
?>

Hogy egyértelmű legyen: a kódot azért írtam, hogy a Folder.jpg fájljaim egységes méretűek legyenek (mindig akkora, mint a legkisebb ilyen kép), és hogy ne kelljen őket “kézzel” méretezgetni. Életszerű példa, ráadásul néha jól tud jönni. A legfontosabb PHP-CLI tulajdonságokat be lehet vele mutatni:

Standard input, output, error
Parancssori programozásnál három kiemelt streamünk van (C-ben például az stdio.h header tartalmazza ezeket): az stdin (bemenet, többnyire billentyűzet), stdout (kimenet, többnyire képernyő), stderr (hibaüzeneteknek, többnyire képernyő). Mindenhol a “többnyire” kifejezést használtam, mivel ezeket módosítani lehet (nem mennék nagyon bele, amit használni fogunk: > az stdout-ot egy fájlba irányítja, < az stdin-t egy fájlból veszi, a |, ami az első program kimenetét adja a második program bemenetéül és a >> ami az stderr-t küldi egy fájlba).

Ha a fenti kódot megnézzük, láthatjuk, hogy egy megfelelően bonyolult könyvtárszerkezettel iszonyatos mennyiségű kiemenetet adhat, de mivel ez a kimenet fontos lehet, érdemes otthagyni a kódban. Ha nem akarjuk nézni, akkor egyszerűen kiadjuk ezt az utasítást:

php folders.php > output.txt

Azonban hogy az élet ne legyen ilyen egyszerű, a streamek – a Kézikönyv által írottak ellenére – nem mindig állnak rendelkezésünkre futáskor, így érdemes így kezdeni a kódjainkat:

1
2
3
4
5
#!/usr/bin/php
<?php
$stdin = (IsSet($stdin) && Is_resource($stdin)) ? $stdin : fopen("php://stdin", "r");
$stdout =  (IsSet($stdout) && Is_resource($stdout)) ? $stdout : fopen("php://stdout", "w");
$stderr =  (IsSet($stderr) && Is_resource($stderr)) ? $stderr : fopen("php://stdout", "w");

Az átirányítások használatával készíthetünk például egy Back-up scriptet:

1
2
3
while($filename = trim(fgets($stdin))) {
/* Felmásoljuk FTP-vel a fájlt */
}

Így csinálhatjuk “kézzel”, elindítjuk a back-up scriptünket, kézzel beírogatjuk a fájlneveket, az meg szépen menti, vagy akár írhatunk egy fájllistát (legyen pl. fajlok.txt) és meghívhatjuk így is:

php backup.php > fajlok.txt

Ezzel már csinálhatunk egy ütemezett feladatot is (ekkor inkább a php-win.exe-t használjuk, az nem nyit parancssor ablakot), ami mondjuk 12 óránként elmenti a fontos fájljainkat. (Részleteket lássuk lentebb!)

Elérési utak
A következő dolog, amit érdemes megjegyeznünk, hogy parancssorból indításkor mindig az aktuális elérési utat használja a PHP, nem azt, ahol a .php fájl van. Hogy ez mit jelent: tegyük fel, hogy a parancssori scriptjeink a C:\Scripts\ mappában vannak, a parancssorban jelenleg a C:\Documents and Settings\Rendszergazda\Dokumentumok\Zene mappában vagyunk.

C:\Documents and Settings\Rendszergazda\Dokumentumok\Zene>php C:\Scripts\Folder.jpg.php > Output.txt

Ez a Zene mappán belül levő összes Folder.jpg fájlt nézi át, az Output.txt is a Zene mappába kerül. Ha megfigyeljük, relatív útvonalról kezdjük a kerestetést (a példában ez a Zene mappa):

find('.', $filename);

Ha ezután átmegyünk a Zene mappából a Filmek mappába, és ott is lefutatjuk, akkor a Filmek mappában levő Folder.jpg-ket méretezi át a kód.

Parancssori paraméterek
Jó szolgálatot tehetnek még az ún. parancssori paraméterek. Ezek azok a paraméterek, amelyeket szóközzel elválasztva szoktunk felsorolni utasítások kiadásakor (pl.: shutdown -r -c “Leállítjuk a gépet!”). Vagyük a következő példát: van egy kódunk, ami készít egy SQL mentést az adatbázisunkról. Általánosabbá tehetjük, ha az adatbázis nevét parancssori paraméterből veszi.

Ezeket a paramétereket az $argv nevű tömbből szerezhetjük meg (az elnevezésről annyit, hogy a C-ben a main függvénynek (ami a program indulásakor meghívásra kerül) nulla (void) vagy két paramétere lehet (int argc, char **argv), az első megadja a paraméterek számát, a második magukat a paramétereket). Hogy ez mit jelent: mentsük el a következő fájlt param.php néven és futassuk le:

1
2
3
4
#!usr/bin/php
<?php
var_dump($argv);
?>

A kimenete a következő lesz:

array(1) {
[0]=>
string(9) “param.php”
}

(Lesz még egy $argc változónk is, de PHP-ben nem valószínű, hogy túlindexelünk egy tömböt és segmens hibával leáll a programunk)

Ha php param.php -r now néven hívjuk, akkor ugyanez a kód a

array(3) {
[0]=>
string(9) “param.php”
[1]=>
string(2) “-r”
[2]=>
string(3) “now”
}

kimenetet adja.

Így már egyszerűen módosíthatjuk a DBBackup.php kódunkat, a

1
mysql_select_db('adatbazisom');

helyett

1
2
3
4
if(!@mysql_select_db($argv[1])) {
fwrite($stderr, "Database (" . $argv[1] . ") not found!");
exit();
}

írva meg is vagyunk. Ezután időzítjük a kódjainkat (mondjuk a “nagyoldal” nevű adatbázist 2 óránként mentjük, ott fontos adatok vannak és gyakran változnak, a “bezartforum” adatbázist (amelynél csak a látogatói statisztika frissül néha) két naponta).

Ütemezés Windows alatt
(Megjegyzés: Linux alatt ütemezhetjük a programjainkat a crontab használatával)

Tegyük fel, hogy szeretnénk a fenti Folder.jpg.php fájlunkat lefutattni minden héten egyszer, mondjuk vasárnap délután. Tegyük fel továbbá, hogy a kód a C:\Scripts mappában van, a célmappa pedig továbbra is a C:\Documents and Settings\Rendszergazda\Dokumentumok\Zene mappa.

Menjünk a Vezérlőpultba (Control Panel), ott válasszuk az Ütemezett Feladatok (Task Scheduler) elemet. A varázsló sajnos buta egy csöppet, így kézzel kell csinálnunk. A megnyíló ablakban üres helyen kattintsunk a jobb gombbal, Új (New), majd Ütemezett Feladat (Task) elemet. Írjunk be valami nevet (pl.: Folder.jpg), majd duplaklikkeljünk az új elemre. A Futtatás (Run) mezőbe írjuk be:

“C:\Program Files\PHP 5\php-win.exe” C:\Scripts\Folder.jpg.php

A futtatás helyéhez a

C:\Documents And Settings\Rendszergazda\Dokumentumok\Zene

útvonalat, majd jelöljük be a “Futtatás csak akkor, ha be van jelentkezve” mezőt, az Ütemezés (Schedule) fül alatt válasszuk a Hetente (Weekly) elemet, válasszuk ki a V feliratú mezőt, Kezdés időpontjának pedig mondjuk 13:00-t. Amint leokéztuk, az ütemezés életbe lép, és minden vasárnap rendezi a Folder.jpg fájljainkat.
(Sajnos az átirányításokra csak a command promtból van lehetőség, így ha ezt használni szeretnénk, akkor közbe kell iktatnunk egy .bat fájl, például:

@echo off
"C:\Program Files\PHP 5\php.exe" C:\Scripts\Folder.jpg.php > out.txt
@echo on

Ezt elmentve valami.bat-ként és a Futtatás mezőbe a bat fájl nevét írjuk, akkor már használhatunk átirányítást, csak csúnya megoldás)

Összefoglalás
A PHP parancssori nyelvként használva nagyon megkönnyítheti a mindennapi adminisztrációt, tanulmányokat (előfordult, hogy ilyen szkripttel töltettem le az egyik kurzus anyagát az előadó weboldaláról, mert lusta voltam kattintgatni a Firefoxban) stb. A PHP-GTK kiegészítve pedig az egyik leghasznosabb eszközünkké válhat.

(További – néhol talán felesleges – információkat a PHP kézikönyv Parancssori programozás a PHP-ben oldalán találhatunk)

6 HOZZÁSZÓLÁS

  1. Ha pedig a kész programunkat olyan jónak itéljük, meg, hogy megosztjuk a világgal megtehetjük. Bonyolult telepitési és egyébb macerák nélkül windowsos .exe formátumban a Bambalam nagysikerü forditójával:
    http://www.bambalam.se/bamcompile/

    Egyenlőre csak php4 -et tud, de ez ne zavarjon minket, nagyon bonyolult kódot úgy sem irunk.

    A php windowson való grafikus felület kiegészitésére pedig tökéletes a WinBinder
    http://winbinder.org/

  2. Köszi a linkeket, majd átnézem őket! (Bár bamcompile PHP 5 supportig szvsz. felejtős…)

    A közeljövőben még írok egy nagyon gyors és felületes tutorialt a PHP-GTK-ról (a példaprogi már meg is van, fut is folyamatosan a gépemen, és iszonyat idegölő, de pont ez volt a cél), utána rendesen átnézem/próbálgatom a winbindert és talán abból is lesz leírás.

    BlackY

  3. Sziasztok!

    Suse linux szerveren próbáltam ki a fent leírtakat. A php script első sorába beírtam: #!usr/bin/php, indítottam a scriptet, és ezt írta ki:
    bash: /srv/www/htdocs/bmb/rssSave.php: cannot execute binary file
    Mi lehet a probléma?

  4. Első körben, hogy a kezdeti / nélkül az relatív útvonal lenne. Ha /usr/bin/php-ben sem indul, akkor meg kell keresned, hogy hol van a php indítófájlja (php):

    find / -name php -type f -print

    utasítás elvileg megmondja.

    BlackY

  5. Kösz BlackY, kipróbáltam, és a keresés a következő utat adta:
    /var/lib/rpm/alternatives/php
    Ezt beírva a php script első sorába (kiegészítve a #!-al persze) a hibaüzenet a következő:
    ” bad interpreter: No such file or directory”.

    Ugyanakkor a scriptet, ha kiveszem belőle az általam beírt első sort, akkor minden további nélkül tudom futtatni a parancssorból a
    php -f “scriptnev”
    parancssal.

    Valamit az elérési útvonal megadásával nagyon benézek, csak nem tudom, hogy mit.

HOZZÁSZÓLOK A CIKKHEZ

Kérjük, írja be véleményét!
írja be ide nevét