Запуск пакетного скрипта через PHP в Windows, не дожидаясь завершения скрипта

Обновлено: см. мой ответ на этот пост для решения этой конкретной проблемы; Я также удалил части вопроса, которые больше не имеют отношения к решению.

Я новичок в PHP и разработке в целом, и я спрашиваю, потому что еще не нашел рабочего решения для своей проблемы.

В рамках проекта я пытаюсь запустить пакетный скрипт. Пакетный скрипт создает несколько файлов анализа и, что особенно важно, компилирует и запускает 2 исполняемых файла, которые мне нужно запустить на потом.

Чтобы запустить скрипт, я составляю вызов $cmd, в котором устанавливаю 2 переменные среды и рабочий каталог, а также добавляю некоторые необходимые параметры, необходимые скрипту для запуска.

затем я бегу

exec($cmd);

Этот вызов создает все файлы в рабочем каталоге и запускает процессы, однако теперь пакетный сценарий ожидает завершения исполняемых процессов, поскольку он очищает некоторые временные файлы после завершения программ. В этот момент программа PHP прекращает выполнение и ожидает завершения пакетного сценария, чего она не делает.

В течение последних 2 дней я пробовал несколько решений, предложенных здесь

или

exec($cmd.' > NUL 2>&1 &')

и

pclose(popen("start /B " . $cmd . " 1> ".$scrptDir." 2>&1 &", "r"));

где $scrptDir — рабочий каталог, описанный ранее.

Я не нашел рабочего решения, в лучшем случае скрипт вообще не запускается, а в худшем выдает синтаксическую ошибку при выполнении AJAX-вызова, что, правда, я пока не понял, почему именно так, ведь AJAX был реализован предшественник и остался довольно недокументированным.

Обновлено: синтаксические ошибки были вызваны мной, потому что я забыл точку с запятой.

Это PHP-код:

    public function runScript() {
        //-- compose the command line to start script 
        $cmd = $this->getCall();

        //-- execute the script
        exec($cmd);        
      
    }
} 

Обновления: Запуск скрипта через

pclose(popen("start /B " . $cmd . " 1> NUL 2>&1 &", "r"));

или

popen("start /B " . $cmd . " 1> NUL 2>&1 &", "r");

или

exec('nohup ' . $cmd . ' 2>&1 &');

имеет тот же результат: сценарий запускается, но исполняемые файлы закрываются, и сценарий выполняет очистку.

С использованием

exec($cmd.' > NUL 2>&1 &');

поведение не отличается от

exec($cmd);

Привет, пожалуйста, исправьте форматирование в вашем вопросе.

S. C. 01.01.2023 18:16

При работе над этим я бы рекомендовал сначала протестировать фиктивный скрипт, чтобы увидеть, как работают эти параметры. Для получения дополнительной помощи вы можете обновить свой пост с точными результатами, которые вы получаете с каждым вариантом.

Markus AO 01.01.2023 18:32

Чтобы помочь вам исправить конкретную проблему с вашим программным кодом, мы ожидаем, что вы отправите все, что нам нужно, чтобы воспроизвести эту проблему в среде того же типа.

Compo 01.01.2023 22:17

@Compo Извините, я добавил код, который сейчас использую для выполнения скрипта. Возможно ли запустить функцию runScript() асинхронно?

gravy 01.01.2023 23:25
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Symfony Station Communiqué - 7 июля 2023 г
Symfony Station Communiqué - 7 июля 2023 г
Это коммюнике первоначально появилось на Symfony Station .
Оживление вашего приложения Laravel: Понимание режима обслуживания
Оживление вашего приложения Laravel: Понимание режима обслуживания
Здравствуйте, разработчики! В сегодняшней статье мы рассмотрим важный аспект управления приложениями, который часто упускается из виду в суете...
Установка и настройка Nginx и PHP на Ubuntu-сервере
Установка и настройка Nginx и PHP на Ubuntu-сервере
В этот раз я сделаю руководство по установке и настройке nginx и php на Ubuntu OS.
Коллекции в Laravel более простым способом
Коллекции в Laravel более простым способом
Привет, читатели, сегодня мы узнаем о коллекциях. В Laravel коллекции - это способ манипулировать массивами и играть с массивами данных. Благодаря...
Как установить PHP на Mac
Как установить PHP на Mac
PHP - это популярный язык программирования, который используется для разработки веб-приложений. Если вы используете Mac и хотите разрабатывать...
0
4
106
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Это один из способов сделать это. Имейте в виду, что это не лучший способ сделать это, особенно если вам нужно что-то сделать с результатами вызова AJAX.

jQuery

Допустим, вы инициируете запрос AJAX следующим образом:

$(document).ready(function() {
    $.ajax({
        type: 'POST',
        url: 'myscript.php',
        data: {'val1':val1,'val2':val2,....},
        timeout: 100,
        success: function(data) {
            // whatever it is you need to do on success, although you could leave it empty
        },
        error: function (xmlhttprequest, textstatus, message, desc, err) {
            if (textstatus === "timeout") {
                // do something else
            } else {
                // cover the case of an error which is not a timeout
            }
        }
    });
});

Ключевой частью здесь является timeout. Это прервет ожидающий AJAX, а это означает, что JS не будет ждать ответа, но скрипт на стороне сервера, вызванный с ним, все равно будет выполняться.


Ванильный Javascript

Если вы не используете jQuery, вы можете сделать то же самое в ванильном Javascript, например.

var xmlHttp = new XMLHttpRequest();
xmlHttp.open("POST", "myscript.php", true);

// Post the request with your data
xmlHttp.send(
    JSON.stringify({
        val1 : val1,
        val2 : val2,
        ... etc
    })
);

xmlHttp.onload = function(e) {
    // Check if the request was a success
    if (this.readyState === XMLHttpRequest.DONE && (this.status === 201 || this.staus === 200)) {
        clearTimeout(xmlHttpTimeout); 
        // do whatever it is you do on successful response
    }
}

// Timeout to abort in 100 miliseconds
var xmlHttpTimeout = setTimeout(ajaxTimeout,100);
function ajaxTimeout(){
   xmlHttp.abort();
   // do what you would normally do once initiating the AJAX
}

Если вы не уверены, достигает ли запрос AJAX серверной части, и нет ли ошибок консоли или сетевых ошибок, вы можете сделать следующее.

Сначала создайте скрипт с именем test.php (или дайте ему другое имя, если у вас уже есть скрипт с таким названием). Затем внутри этого скрипта поместите следующее.

<?php

sleep(15);
$filename = "zztest.txt";
$file = fopen($filename,'a+');

fwrite($file, date("Y-m-d H:i:s"));
sleep(5);
fwrite($file, PHP_EOL.date("Y-m-d H:i:s").PHP_EOL."********".PHP_EOL);
fclose($file);

?>

Что это будет делать:

  1. После инициализации подождите 15 секунд (это то, что делает sleep(15))
  2. Создайте (или откройте, если он уже существует) файл *.txt и запишите в него текущую дату и время
  3. Держите файл открытым — обратите внимание, что после fwrite(...) нет fclose(...). Как правило, это не очень хорошая практика, потому что что-то может нарушить выполнение скрипта, и файл останется заблокированным, но в данном случае это нормально, потому что он предназначен для тестирования.
  4. Как только дата и время будут записаны в текстовый файл, подождите еще 5 секунд (sleep(5)), а затем запишите в него текущую дату и время. PHP_EOL — символ новой строки
  5. Наконец, закройте файл

Следующий шаг — заменить вызов myscript.php внутри вашего AJAX, чтобы он вызывал test.php. Остальная часть вашей настройки AJAX может остаться прежней.

Затем выполните AJAX, как обычно. Подождите около 20 секунд и проверьте, был ли создан файл с именем zztest.txt, и если да, то каково его содержимое.

Если нет, то проблема во внешнем интерфейсе (вызов AJAX).

Я попытался использовать опубликованный вами ванильный javascript, и запрос проходит и запускает скрипт, затем срабатывает тайм-аут. Однако я все еще не могу выполнить какой-либо PHP-код, он все еще ждет завершения сценария.

gravy 02.01.2023 10:56

Пожалуйста, смотрите обновленный ответ. Я добавил для вас способ проверить, работает ли AJAX. Если это так (то, что я объяснил в обновлении, действительно происходит), то проблема может быть в вашем исходном PHP-скрипте. Если код исходного PHP-скрипта необходимо продолжить после вызова пакетного скрипта, вы можете рассмотреть возможность разделения PHP-кода на два отдельных файла. Это, однако, может не сработать для вас, если код PHP под exec($cmd) полагается на его результаты.

FiddlingAway 02.01.2023 11:14

Скрипт создал текстовый файл и записал в него: " 2023-01-02 11:26:15 2023-01-02 11:26:20 ******** ", поэтому я думаю, что проблема заключается в PHP-код.

gravy 02.01.2023 11:29

Вот кое-что еще, что можно попробовать: в верхней части исходного PHP-скрипта добавьте эти две строки кода: error_reporting(E_ALL); ini_set("display_errors",1); Они включат ведение журнала ошибок. Если вы включаете или требуете какие-либо дополнительные файлы в своем PHP-скрипте (в самом верху), поместите две строки под ними. Кроме того, в целях тестирования временно закомментируйте строку кода, которая вызывает пакетный скрипт. Это делается для того, чтобы он не запустился, так как сейчас вы просто ищете ошибки. Если проблема не устранена, я предлагаю открыть еще один вопрос на SO.

FiddlingAway 02.01.2023 11:52

Где будут отображаться эти ошибки? Я использую XAMPP.

gravy 02.01.2023 15:20

Они должны отображаться в ответе AJAX — вы можете открыть раздел «Сеть» инструментов разработчика, нажав CTRL + SHIFT + P (Chrome) или CTRL + SHIFT + E (Firefox). После открытия раздела «Сеть» активируйте AJAX и дождитесь его появления. Когда вы нажмете на нее, справа откроется боковая панель. Найдите в нем вкладку Response и проверьте содержимое.

FiddlingAway 02.01.2023 15:25
Ответ принят как подходящий

Хорошо, я нашел обходной путь.

Вместо

exec($cmd)

я использовал

$filename = "help.bat";
$file = fopen($filename,'a+');
fwrite($file, $cmd);
fclose($file);
pclose(popen("start /B ". $filename, "r"));

Я записал составленную команду $cmd в пакетный файл, а затем вместо этого выполнил этот пакетный файл. Я думаю, что причина, по которой это работает сейчас, заключается в том, что мой составленный вызов был слишком сложным для меня, поэтому я попытался сделать его проще.

Другие вопросы по теме