Няколко трика за писане на CLI PHP скриптовe
Въпреки, че PHP се използва главно за уеб приложения, той е също много удобен за писане на скриптове за командния ред. Особено ако човек е достатъчно навътре, PHP може напълно да замести традиционните фаворити в тази област - bash и Perl. За да демонстрираме това нека разгледаме следния скрипт за архивиране.
Изисквания
Скриптът ще приема списък от файлове и директории, които да се архивират, и директория, където да стоят архивите. За удобство ще използваме два вида за входните данни: комбинация от параметри на командния ред и стандартния вход или файл с конфигурация.
Извеждане на съобщения
За начало създаваме прост клас за съобщения, който извежда грешките на STDERR, а нормалните съобщения - на STDOUT.
class SimpleLogger {
function info($message) {
fprintf(STDOUT, date('[Y-m-d H:i:s] ') . $message . "\n");
}
function error($message) {
fprintf(STDERR, date('[Y-m-d H:i:s] ') . $message . "\n");
}
}
Основната функционалност
class BackupCreator {
private $dirs;
private $backup_dir;
private $test_run;
private $logger;
function BackupCreator($dirs, $backup_dir, $test_run=false) {
$this->dirs = $dirs;
$this->backup_dir = $backup_dir;
$this->test_run = $test_run;
$this->logger = new SimpleLogger();
}
function backup() {
if (!is_dir($this->backup_dir)) {
$this->logger->error("Directory {$this->backup_dir} does not exist or is not accessible!");
return -1;
}
$backup_timestamp = date("YmdHis");
foreach ($this->dirs as $dir) {
if (!file_exists($dir)) {
$this->logger->error("Backup entry {$dir} does not exist or is not accessible!");
continue;
}
$archive_name = $this->backup_dir . '/' .$backup_timestamp . str_replace('/', '_', $dir);
$folder = dirname($dir);
$item = basename($dir);
chdir($folder);
$cmd = "tar -zcf '$archive_name' '$item'";
$this->execute($cmd);
}
return 0;
}
function execute($cmd) {
$this->logger->info("Executing $cmd");
}
}
Изпълнение на външни програми
Ще използваме комбинация от tar и gzip, за да компресираме архивите. За целта създаваме функция за изпълнение на външни команди, която предлага подробни съобщения и опция за тестово изпълнение - за проверка на изпълняваните команди и предотвратяване на неволни грешки.
function execute($cmd) {
$this->logger->info("Executing: $cmd");
if ($this->test_run) {
$this->logger->info("Test mode - command is not executed");
return;
}
exec($cmd, $output, $return_value);
$this->logger->info("Command completed with exit code $return_value");
if ($output) {
$this->logger->info("Command output is:\n" . implode("\n", $output));
}
}
Четене на аргументите от командния ред и стандартния вход
След като имаме основната функционалност започваме с четенето на аргументите. За целта ползваме функцията getopt. Нашият скрипт ще приема следните опции:
-d <dir> - директорията за архивите е <dir>, а самите файлове и директории за архивиране се четат от стандартния вход STDIN, по един на ред
-c <file> - използва се конфигурационен файл (това е с по-висок приоритет от -d)
-t - тестово изпълнение
Обърнете внимание на двоеточията след опциите, които очакват аргумент.
function usage() {
echo "Available options:
-d <dir> - location of backup dir is <dir>, and backup items list is read from STDIN, one on each line
-c <file> - use configuration file (this option overrides -d)
-t - test run
";
exit(-1);
}
$opts = getopt('d:c:t');
if (!$opts) {
usage();
}
$dirs = array();
$backup_dir = '';
$test_run = isset($opts['t']);
if (isset($opts['c']) && $opts['c']) {
//TODO
} else if (isset($opts['d']) && $opts['d']) {
$backup_dir = $opts['d'];
while ($item = fgets(STDIN)) {
$dirs[] = trim($item);
}
} else {
usage();
}
if (!$dirs || !$backup_dir) {
usage();
}
$backup = new BackupCreator($dirs, $backup_dir, $test_run);
$backup->backup();
Сега можем да опитаме скрипта. Изпълнете:
php backup.php -d /tmp -t
и въведете няколко файла и директории, всеки на отделен ред. Накрая натиснете CTRL+D. Получава се подобен изход:
[2010-02-02 01:42:19] Executing: tar -zcf '/tmp/20100202014219_home_foo_Desktop' 'Desktop'
[2010-02-02 01:42:19] Test mode - command is not executed
[2010-02-02 01:42:19] Backup entry /home/xxx does not exist or is not accessible!
Четене на ini файл
Сега последната част: четене на входа от конфигурационен файл. Форматът е следния:
backup_dir=/tmp
dirs[]=/home/foo/Desktop
dirs[]=/home/bar/Documents
dirs[]=/home/xxx
Тук използваме функцията parse_ini_file
if (isset($opts['c']) && $opts['c']) {
$config = parse_ini_file($opts['c'], true);
if (!$config) {
fprintf(STDERR, "Error while parsing config file {$opts['c']}");
exit(-1);
}
$backup_dir = $config['backup_dir'];
$dirs = $config['dirs'];
}
Два примера за използване
Архивиране на всички файлове и директории, които започват с 'documents' в домашната папка на foo, като архивите отиват в /tmp:
find /home/foo -name documents* | php backup.php -d /tmp
Създаване на ежедневни архиви на папките public_html на потребителите foo и bar. Първо създаваме конфигурационен файл public_html_backups.ini:
backup_dir=/backups
dirs[]=/home/foo/public_html
dirs[]=/home/bar/public_html
След това добавяме следното в crontab на root:
1 2 * * * php backup.php -c public_html_backups.ini
Удобното тук е, че добавянето на нов потребител е само въпрос на редактиране на конфигурационния файл, без промени по кода и crontab
Свалете целия скрипт
Няма коментари
Обратно към списъка със статиите
Тази страница последно е променяна на 2025-04-30 15:13:51