如何通过PHP正常运行PowerShell脚本?
问题:PHP调用PowerShell脚本未执行预期操作
1. 场景与现象
通过PHP代码调用PowerShell脚本时,脚本未执行预期的哈希计算与数据库插入操作,但直接在命令行用相同参数启动脚本则完全正常。
PHP调用代码
$hashingCommand = "start powershell -file C:\Apache24\htdocs\\runHashCalc.ps1 \\\$sAddr \\\$dAddr $name $algorithm"; pclose(popen($hashingCommand, 'r'));
WMIC捕获的实际执行命令
C:\>WMIC path win32_process get Caption,ProcessId,Commandline | findstr "testingCopyJob" powershell.exe powershell -file C:\Apache24\htdocs\runHashCalc.ps1 \\ACTUALPATH\netstorage\User \\ACTUALPATH\netstorage\copyfolder testingCopyJob sha256 5036
命令行直接执行的正常输出
C:\>powershell -file C:\Apache24\htdocs\runHashCalc.ps1 \\ACTUALPATH\netstorage\User \\ACTUALPATH\netstorage\copyfolder testingCopyJob sha256 \\config.sample.inc.php sha256 mariadb --user=username --password=******* -e 'INSERT INTO testingCopyJob (file_name,hash) VALUES (' \\config.sample.inc.php',' 8AA4CE89D12978E805C6A128745A1A885A1E6523220C6647886DC74DC13F421A')' *****_xsw [...]
2. PowerShell脚本核心代码
[...] gci -af -rec $sourceFolder | Foreach-Object { $cmd = Get-FileHash $_.FullName -Algorithm $algorithm | Format-List $asString = $cmd | ft | out-string $answerArray = $asString.Split([System.Environment]::NewLine,[System.StringSplitOptions]::RemoveEmptyEntries) $hash = $answerArray[1].Split(":")[1] $path = $answerArray[2] -split ':',2 $aPath = $path[1] $aPath=$aPath.Replace($sourceFolder,'') $aPath=$aPath.Replace('\','\\') write-host $aPath | Out-File -FilePath C:\Apache24\htdocs\hashlog.log -Append write-host $algorithm | Out-File -FilePath C:\Apache24\htdocs\hashlog.log -Append write-host "mariadb --user=user --password=******* -e 'INSERT INTO $name (file_name,hash) VALUES ('$aPath','$hash')' *****_xsw" | Out-File -FilePath C:\Apache24\htdocs\hashlog.log -Append $answer = mariadb --user=user --password=******* -e "INSERT INTO $name (file_name,hash,source) VALUES ('$aPath','$hash',0)" *****_xsw write-host $answer | Out-File -FilePath C:\Apache24\htdocs\hashlog.log -Append } [...]
注:原脚本用write-host无法生成日志,改用write-output后日志正常,但核心操作仍未执行。
3. 故障原因分析
- 权限不匹配:PHP运行在Apache服务账户(通常为
NT AUTHORITY\SYSTEM或IUSR)下,该账户可能无网络共享路径访问权限、MariaDB写入权限,或部分目录操作权限。 - 参数传递异常:WMIC显示命令末尾多了
5036(疑似进程ID被错误追加),导致脚本接收的参数数量超出预期,逻辑执行出错。 - 执行环境差异:服务账户的PowerShell环境未加载必要配置,或
mariadb不在其PATH变量中,导致命令无法找到。 - 字符串解析缺陷:通过解析
Format-List/ft的格式化输出来提取哈希和路径,依赖输出格式稳定性,一旦环境(如本地化)变化就会解析失败,无法获取正确的$hash和$aPath。
4. 解决办法
修复权限问题
- 给Apache服务账户授予网络共享路径的读取权限、MariaDB数据库的写入权限,确保日志目录的写入权限。
修正参数传递
修改PHP代码,避免start命令导致的额外参数追加,用escapeshellarg处理参数转义:
$sAddrEscaped = escapeshellarg($sAddr); $dAddrEscaped = escapeshellarg($dAddr); $nameEscaped = escapeshellarg($name); $algorithmEscaped = escapeshellarg($algorithm); $hashingCommand = "powershell.exe -NoProfile -ExecutionPolicy Bypass -File C:\\Apache24\\htdocs\\runHashCalc.ps1 $sAddrEscaped $dAddrEscaped $nameEscaped $algorithmEscaped"; pclose(popen($hashingCommand, 'r'));
统一执行环境
- 在PowerShell脚本中指定
mariadb的完整路径(如C:\Program Files\MariaDB\bin\mariadb.exe),避免依赖PATH变量。 - 添加
-NoProfile参数,避免加载不必要的用户配置,保证执行环境一致。
优化字符串处理逻辑
放弃解析格式化输出,直接使用Get-FileHash的对象属性,同时修复SQL注入风险:
gci -af -rec $sourceFolder | Foreach-Object { $fileHash = Get-FileHash $_.FullName -Algorithm $algorithm $hash = $fileHash.Hash.Trim() $aPath = $_.FullName.Replace($sourceFolder, '') $aPath = $aPath.Replace('\', '\\').Trim() # 输出日志 Write-Output $aPath | Out-File -FilePath C:\Apache24\htdocs\hashlog.log -Append Write-Output $algorithm | Out-File -FilePath C:\Apache24\htdocs\hashlog.log -Append # 转义单引号避免SQL注入 $safePath = $aPath -replace "'", "''" $sql = "INSERT INTO $name (file_name,hash,source) VALUES ('$safePath','$hash',0)" Write-Output "mariadb --user=user --password=******* -e `"$sql`" *****_xsw" | Out-File -FilePath C:\Apache24\htdocs\hashlog.log -Append $answer = & "C:\Program Files\MariaDB\bin\mariadb.exe" --user=user --password=******* -e $sql *****_xsw Write-Output $answer | Out-File -FilePath C:\Apache24\htdocs\hashlog.log -Append }
内容的提问来源于stack exchange,提问作者Monocito
相关产品推荐
相关产品推荐

