spuštění batch souboru pomocí exec()

- Fajmy
- Člen | 10
Ahoj,
mám funkci, která využívá exec(), nicméně jsem zjistil, že mi to v Nette nejede. Zkoušel jsem v čistém PHP a funkce funguje jak má. Chci se tedy zeptat, jestli dělám něco špatně, nebo se spuštění externího programu v consoli řeší jinak.
Mám složku lflc, ve které jsou všechny potřebné soubory k dávkovému souboru lflc-run.bat, tuto složku mám ve www a následně exec volám takto:
exec("lflc/lflc-run.bat");

- Fajmy
- Člen | 10
Mám samostatnou třídu, ve které je jedna funkce a v té volám funkci
exec(). Přikládám celou funkci:
class Lflc
{
public function lflc($v1, $v2, $v3){
$vystup = 0;
$inputFile = "lflc/data.txt";
$fh = fopen($inputFile, 'w') or die("nelze otevrit vstupni soubor");
$hlavicka_promenne = 'V1'."\t".'V2'."\t".'V3'."\r\n";
//Zapsani dat do vstupniho souboru
fwrite($fh, $hlavicka_promenne);
$v1_text = $v1."\t";
$v2_text = $v2."\t";
$v3_text = $v3."\t";
fwrite($fh, $v1_text);
fwrite($fh, $v2_text);
fwrite($fh, $v3_text);
fclose($fh);
//Spusteni LFLC
exec("lflc/lflc-run.bat");
$outputFile = "lflc/output.txt";
$fh = fopen($outputFile, 'r');
$output = fread($fh, filesize($outputFile));
fclose($fh);
//Nacteni vystupu
$values = explode("\t",$output);
dump($values);
$vystup_array = explode("\n",$values[6]);
$vystup_val = $vystup_array[0];
//LFLC dava cislo ve forme O,5 musim upravit na 0.5
$vystup_val = str_replace(",",".",$vystup_val);
dump($vystup_val);
return $vystup_val;
}
}
To volám v Manageru na testovací data:
$this->lflc->lflc('0', '0', '0');
$this->lflc->lflc('4', '0,7', '1,1');
Krok načtení, otevření a zápis dat do inputFile proběhne. Řádek
s funkcí exec() jak kdyby se přeskočil. A načte se outputFile
a vypíše mi z něho data – nicméně se nikdy nezmění, jsou tam pořád
data, které jsem zkoušel v PHP.
Ano, soubor bat se nespouští.

- uestla
- Backer | 799
Do input souboru se ti to zapíše v pořádku jelikož ta relativní cesta
k input souboru se vyhodnotí jinak než relativní cesta k tomu baťáku,
který voláš z execu – v exec() se tuším vyhodnocuje proti
aktuálnímu „pracovnímu adresáři“ (current working directory), což
pokud to voláš zevnitř Nette, by měl být adresář, ve kterém máš
index.php.
Proto je lepší přístup mít místo relativní cesty absolutní – máš jistotu, že spouštíš správný skript a usnadní ti to případný budoucí přesun třídy / souboru, ze kterého daný skript voláš:
# common.neon
services:
- Lflc(%appDir%/../cesta-k-lflc-slozce)
class Lflc
{
private string $binDir;
public function __construct(string $binDir)
{
$this->binDir = $binDir;
}
public function lflc($v1, $v2, $v3): void
{
// ...
exec(escapeshellcmd(sprintf('%s/lflc-run.bat', $this->binDir)), $output, $status);
dump($output, $status);
// ...
}
}

- Fajmy
- Člen | 10
Pořád se mi to nějak nedaří. Tu cestu musím mít ke složce ve které
je ten baťák, pravda ?
To service Lflc je cesta k mé vytvořené třídě a já tam přidám tu cestu
do závorky? Ttedy v commonu to pak vypadá takto?
service:
- App\Services\Lflc(%appDir%/../Services/lflc)
to poslední lflc je pak složka, která obsahuje ten baťák. I tak to stále nefunguje.
Přesouvám tu složku z místa na místo a zkouším, ale nedaří se. Je správně, že je složka lflc, která obsauje baťák ve složce www ? Nemá být třeba jinde, abych na ten baťák dosáhl, nebo to je jedno ? Teď je ta složka s lflc ve složce Services, ve které je i třída Lflc.
Mám pocit, že jsem se do toho trochu zamotal.

- uestla
- Backer | 799
To rozmotáme :-)
Pokud máš tu složku lflc přímo ve složce s třídou Lflc, tak bude nejpřímočařejší do toho původní volání přidat absolutní cestu:
exec(escapeshellcmd(__DIR__ . '/lflc/lflc-run.bat'));
Za šťastnější bych ale považoval si danou cestu předat jako závislost, jak jsem popisoval výše.

- Fajmy
- Člen | 10
Tak jsem vyzkoušel a nic. Složku lflc ve které je baťák mám teda ve složce kde mám i třídu Lflc.
Takhle vypadá ten kus kódu:
exec(escapeshellcmd(__DIR__ . '/lflc/lflc-run.bat'), $out, $var);
dump($out, $var);
A tohle dostávám na výsledek:
array
0 => ""
1 => "D:\XAMPP\htdocs\DIPL\www>D:\XAMPP\htdocs\DIPL\www\hierarchic_base.exe -k D:\XAMPP\htdocs\DIPL\www\test_db.knb -i D:\XAMP\htdocs\DIPL\www\data.txt -o ... " (185)
1
Napadlo mě, nepotřebuji nějakou speciální knihovnu k tomu abych mohl
spouštět exec() v Nette ?

- uestla
- Backer | 799
Tak to už vypadá, že se ten baťák volá – výstup ti ale Dumper ořízne (má celkem 185 znaků), takže nevidíš celou tu hlášku, ale evidentně dochází k nějaké chybě až uvnitř toho batch scriptu (voláš tam nějaký hierarchic_base.exe atd.).
Dumpni si to neořezané, třeba ti to prozradí víc:
Tracy\Debugger::$maxLength = false;
dump($out, $var);

- Fajmy
- Člen | 10
Po vyzkoušení dostávám takovouto hlášku: "":https://ctrlv.cz/ZoLw
Baťák vypadá takto:
@echo off
::
:: Environment settings
::
set dir=%cd%
set lflc=%dir%\hierarchic_base.exe
::
:: SGS LFLC default files
::
:: LFLC knowledge base file
set kbn=test_db.knb
:: LFLC input file
set input=data.txt
:: LFLC output file
set output=output.txt
::
:: LFLC execution
::
@echo on
%lflc% -k %dir%\%kbn% -i %dir%\%input% -o %dir%\%output%
Jedná se o simulaci expertního systému, vložím vstupní data do inputu
→ provede se baťák → a ten mi vyplivne výsledky podle expertního
systému vytvořeného právě v programu LFLC.
A jak jsem psal na začátku, tak když si vytvořím obyčejný php projekt,
tak mi to jde spustit a vše proběhne v pořádku.

- uestla
- Backer | 799
Je to pořád o té cestě k souboru – při spouštění v rámci
projektu máš working directory nastavenou na www složku.
V tom batchi se na řádku 6 používá CWD, tím pádem ti to nejspíše
spadne na tom, že to nenajde hierarchic_base.exe (pro zbytek té
chybové zprávy si nastav Debugger::$maxLength = null, s tím
false jsem se přepsal, tak se to dělalo dříve).
Možná bude pro tebe nejjednodušší nastavit v tom execu ještě CWD:
exec(escapeshellcmd(sprintf('cd %s && lflc/lflc-run.bat', __DIR__)));
Tím pádem se to přepne do té složky, a jak exec(), tak samotný batch skript budou operovat se správnou working directory.

- Fajmy
- Člen | 10
Zkusil jsem a někam se to pohlo. První spuštění po změně, mi tento příkaz vrátil prázdné pole a 1:
exec(escapeshellcmd(sprintf('cd %s && /lflc/lflc-run.bat', __DIR__)), $out, $var);
dump($out, $var);
Pak jsem zkusil tuhle variantu
exec(__DIR__.'/lflc/lflc-run.bat', $o, $v);
dump($o, $v);
a to se už načítalo déle, po prvním spuštěním vyskočilo cmd a napsalo se že hieararchic-base.exe přestalo pracovat. Následně mi vyskočilo tohle:
array
0 => ''
1 => 'D:\XAMPP\htdocs\DIPL\www>D:\XAMPP\htdocs\DIPL\app\Services\lflc\\hierarchic_base.exe -k D:\XAMPP\htdocs\DIPL\app\Services\lflc\\test_db.knb -i D:\XAMP … output.txt'
3
Tento výstup dostávám teď po každém spuštění ikdyž se nezapne cmd a nevyskočí hláška že přestalo pracovat.