Создание консольных сценариев PHP на операционной системе Linux
Основы написания PHP скриптов для командной строки в Linux
Наиболее эффективным способом создания исполняемого PHP скрипта под Linux является использование shebang и установка прав на выполнение. Такой подход позволяет запускать скрипт без явного указания интерпретатора.
#!/usr/bin/env php
<?php
echo "Hello, Linux!\n";скрипт php linux (написание скрипта на php для linux)
После сохранения файла (например, ~/hello.php) необходимо сделать его исполняемым:
chmod +x ~/hello.php
Теперь запуск осуществляется командой ./hello.php. При этом вывод будет направлен в терминал.
Распространенная ошибка: если первый символ shebang указан неверно (например, лишний пробел) или файл не имеет прав на выполнение, появится сообщение "command not found". Решение: проверить shebang (должен быть #!/usr/bin/env php) и права chmod +x.
Проблема кодировки: если скрипт содержит кириллицу, терминал может отображать кракозябры. Решение: установить локаль (export LANG=ru_RU.UTF-8) или использовать в коде iconv/mb_convert_encoding.
Как передать аргументы командной строки в PHP скрипт?
Для работы с аргументами доступны глобальные массивы $argv и $argc. Первый элемент $argv[0] содержит имя скрипта. Пример:
#!/usr/bin/env php
<?php
echo "Количество аргументов: $argc\n";
foreach ($argv as $i => $arg) {
echo "arg[$i] = $arg\n";
}
Запуск ./script.php foo bar выведет:
Количество аргументов: 3 arg[0] = ./script.php arg[1] = foo arg[2] = bar
Ошибка: если передается аргумент, содержащий пробелы, его нужно заключать в кавычки. Иначе каждый отдельный фрагмент станет отдельным аргументом.
Как читать ввод с клавиатуры в консольном PHP?
Для чтения из STDIN используется функция fgets(STDIN). Пример интерактивного запроса имени:
#!/usr/bin/env php
<?php
echo "Введите ваше имя: ";
$name = trim(fgets(STDIN));
echo "Привет, $name!\n";
Также можно читать многострочный ввод, используя цикл до EOF (Ctrl+D).
Проблема: если скрипт запускается из конвейера (pipe), fgets(STDIN) прочитает переданные данные, что может быть неочевидно. Для интерактивного режима следует проверять, что STDIN является терминалом.
Как обрабатывать длинные опции (--option) и короткие (-o) в PHP?
Для разбора опций используется функция getopt(). Пример:
#!/usr/bin/env php
<?php
$options = getopt("f:v", ["file:", "verbose"]);
if (isset($options['file']) || isset($options['f'])) {
$file = $options['file'] ?? $options['f'];
echo "Обрабатываем файл: $file\n";
}
if (isset($options['v']) || isset($options['verbose'])) {
echo "Режим подробного вывода включен\n";
}
Запуск ./script.php --file=log.txt -v даст соответствующий вывод.
Типичная ошибка: если опция требует значение, но оно не указано, getopt вернет пустую строку или false. Следует проверять isset перед использованием.
Как создать фоновый процесс (демон) на PHP?
Для перевода скрипта в фоновый режим используется комбинация posix_setsid() и отключение от терминала. Пример простого демона:
#!/usr/bin/env php
<?php
$pid = pcntl_fork();
if ($pid == -1) {
die("Ошибка форка");
} elseif ($pid) {
exit(0); // Родитель завершается
}
posix_setsid(); // Создает новую сессию
// Перенаправление STDIN/STDOUT/STDERR в /dev/null
fclose(STDIN);
fclose(STDOUT);
fclose(STDERR);
$stdin = fopen('/dev/null', 'r');
$stdout = fopen('/var/log/mydaemon.log', 'ab');
$stderr = fopen('/var/log/mydaemon_error.log', 'ab');
// Основной цикл
while (true) {
// ... работа ...
sleep(10);
}
Такой скрипт после запуска отсоединится от терминала и будет работать в фоне.
Проблема: демон может "повиснуть" при ошибке. Необходимо обрабатывать сигналы (SIGTERM, SIGINT) для корректного завершения. Также нужно отслеживать родительские процессы и избегать зомби.
Как использовать библиотеку Symfony Console для создания CLI команд?
Symfony Console - стандарт для построения консольных приложений. Установка через Composer: composer require symfony/console. Пример приложения:
#!/usr/bin/env php
<?php
require __DIR__.'/vendor/autoload.php';
use Symfony\Component\Console\Application;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
class GreetCommand extends Command
{
protected static $defaultName = 'greet';
protected function configure()
{
$this->setDescription('Приветствует пользователя')
->addArgument('name', InputArgument::REQUIRED, 'Имя пользователя');
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$name = $input->getArgument('name');
$output->writeln("Hello, $name!");
return Command::SUCCESS;
}
}
$app = new Application('MyApp', '1.0');
$app->add(new GreetCommand());
$app->run();
Запуск ./app.php greet John.
Ошибка: если не установлены зависимости Composer, скрипт не запустится. Следует выполнить composer install.
Расширенный пример 1: Парсинг лог-файла с использованием getopt и файловых операций
#!/usr/bin/env php
<?php
$options = getopt("f:o:", ["file:", "output:", "sort", "reverse"]);
if (empty($options['file']) && empty($options['f'])) {
fwrite(STDERR, "Не указан входной файл. Используйте --file или -f.\n");
exit(1);
}
$inFile = $options['file'] ?? $options['f'];
if (!file_exists($inFile)) {
fwrite(STDERR, "Файл $inFile не найден.\n");
exit(1);
}
$outFile = $options['output'] ?? $options['o'] ?? 'php://stdout';
$lines = file($inFile, FILE_IGNORE_NEW_LINES);
if (isset($options['sort'])) {
sort($lines);
}
if (isset($options['reverse'])) {
$lines = array_reverse($lines);
}
file_put_contents($outFile, implode("\n", $lines));
echo "Готово. Вывод в $outFile\n";
$ ./parse.php --file input.txt --sort --reverse -o output.txt Готово. Вывод в output.txt
Этот скрипт читает строки из файла, при необходимости сортирует и/или переворачивает, записывает результат. Используется STDERR для сообщения об ошибках.
Расширенный пример 2: Демон с корректной обработкой сигналов
#!/usr/bin/env php
<?php
// Установка обработчика сигналов
declare(ticks = 1);
$stop = false;
pcntl_signal(SIGTERM, function () use (&$stop) {
$stop = true;
});
pcntl_signal(SIGINT, function () use (&$stop) {
$stop = true;
});
// Форк и отсоединение
$pid = pcntl_fork();
if ($pid == -1) die("Ошибка форка");
if ($pid) exit(0);
posix_setsid();
fclose(STDIN); fclose(STDOUT); fclose(STDERR);
fopen('/dev/null', 'r');
$stdout = fopen('/var/log/daemon.log', 'ab');
$stderr = fopen('/var/log/daemon.err', 'ab');
// Основной цикл
while (!$stop) {
fwrite($stdout, date('Y-m-d H:i:s') . " - Работа демона\n");
sleep(10);
}
fwrite($stdout, "Демон завершен\n");
exit(0);
Обработчики сигналов меняют флаг, основной цикл проверяет его и корректно завершается. Запускать из командной строки: ./daemon.php </dev/null &
Расширенный пример 3: Параллельная обработка данных с помощью parallel
#!/usr/bin/env php
<?php
if (!extension_loaded('parallel')) {
fwrite(STDERR, "Требуется расширение parallel.\n");
exit(1);
}
$threads = 4;
$tasks = range(1, 20); // задача: вычислить квадрат числа
$worker = function ($num) {
$result = $num * $num;
sleep(1); // имитация долгой работы
return $result;
};
$promises = [];
foreach ($tasks as $num) {
$promises[] = \parallel\run($worker, [$num]);
}
$results = [];
foreach ($promises as $promise) {
$results[] = $promise->value();
}
echo "Результаты: ";
print_r($results);
$ ./parallel_example.php
Результаты: Array
(
[0] => 1
[1] => 4
[2] => 9
...
[19] => 400
)
Этот пример демонстрирует использование parallel для параллельного выполнения задач. Каждая задача выполняется в отдельном потоке. Необходимо расширение parallel (устанавливается через pecl install parallel).
Расширенный пример 4: Интерактивное CLI-приложение с readline
#!/usr/bin/env php
<?php
while (true) {
$command = readline("MyApp> ");
if ($command === false) break; // Ctrl+D
readline_add_history($command);
if (in_array($command, ['exit', 'quit'])) break;
if ($command === 'help') {
echo "Доступные команды: help, echo, date, exit\n";
} elseif (strpos($command, 'echo ') === 0) {
echo substr($command, 5) . "\n";
} elseif ($command === 'date') {
echo date('Y-m-d H:i:s') . "\n";
} else {
echo "Неизвестная команда. Введите help.\n";
}
}
readline обеспечивает историю комманд и редактирование строки.