批量启动.ps1 | BatchRunner.ps1
<#
.SYNOPSIS
批处理文件运行器服务管理脚本
.DESCRIPTION
用于启动当前目录下的所有bat/ps1文件和快捷方式,并提供服务注册和卸载功能
.PARAMETER Action
操作类型: install (安装服务), uninstall (卸载服务), run (直接运行), start (启动服务), stop (停止服务)
#>
param(
[Parameter(Mandatory=$false)]
[ValidateSet('install', 'uninstall', 'run', 'start', 'stop', 'service')]
[string]$Action = 'run'
)
# 设置控制台编码为UTF-8
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
$OutputEncoding = [System.Text.Encoding]::UTF8
# 配置变量
$ServiceName = "BatchRunnerService"
$ServiceDisplayName = "Batch Files Runner Service"
$ServiceDescription = "Auto run all batch files and PowerShell scripts in specified directory"
$CurrentPath = $PSScriptRoot
if ([string]::IsNullOrEmpty($CurrentPath)) {
$CurrentPath = Get-Location
}
$LogFile = Join-Path $CurrentPath "service.log"
#region 函数定义区域
# 函数: 写日志
function Write-Log {
param([string]$Message)
$timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
$logMessage = "$timestamp - $Message"
try {
# 使用UTF8编码写入日志
Add-Content -Path $LogFile -Value $logMessage -Encoding UTF8 -ErrorAction SilentlyContinue
}
catch {
# 忽略日志写入错误
}
Write-Host $logMessage
}
# 函数: 解析快捷方式文件
function Get-ShortcutTarget {
param([string]$ShortcutPath)
try {
$shell = New-Object -ComObject WScript.Shell
$shortcut = $shell.CreateShortcut($ShortcutPath)
$targetPath = $shortcut.TargetPath
$arguments = $shortcut.Arguments
$workingDirectory = $shortcut.WorkingDirectory
# 创建返回对象
$result = @{
TargetPath = $targetPath
Arguments = $arguments
WorkingDirectory = $workingDirectory
}
# 释放COM对象
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($shortcut) | Out-Null
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($shell) | Out-Null
[System.GC]::Collect()
[System.GC]::WaitForPendingFinalizers()
return $result
}
catch {
Write-Log "解析快捷方式失败: $ShortcutPath - $_"
return $null
}
}
# 函数: 判断文件类型
function Get-ScriptType {
param([string]$FilePath)
if ($FilePath -match '\.bat$') {
return 'BAT'
}
elseif ($FilePath -match '\.ps1$') {
return 'PS1'
}
else {
return 'UNKNOWN'
}
}
# 函数: 运行所有脚本文件和快捷方式
function Run-AllBatchFiles {
Write-Log "开始扫描目录: $CurrentPath"
# 获取当前目录下所有bat文件
$batFiles = Get-ChildItem -Path $CurrentPath -Filter "*.bat" -File
# 获取当前目录下所有ps1文件(排除当前脚本本身)
$ps1Files = Get-ChildItem -Path $CurrentPath -Filter "*.ps1" -File | Where-Object {
$_.FullName -ne $MyInvocation.PSCommandPath -and
$_.Name -ne 'BatchRunner.ps1' -and
$_.Name -ne '批量启动.ps1'
}
# 获取当前目录下所有lnk文件(快捷方式)
$lnkFiles = Get-ChildItem -Path $CurrentPath -Filter "*.lnk" -File
Write-Log "扫描到 $($batFiles.Count) 个bat文件, $($ps1Files.Count) 个ps1文件, $($lnkFiles.Count) 个lnk文件"
# 筛选出指向bat/ps1文件的快捷方式
$scriptLnkFiles = @()
foreach ($lnk in $lnkFiles) {
Write-Log "正在解析快捷方式: $($lnk.Name)"
$shortcutInfo = Get-ShortcutTarget -ShortcutPath $lnk.FullName
if ($shortcutInfo -and -not [string]::IsNullOrEmpty($shortcutInfo.TargetPath)) {
$scriptType = Get-ScriptType -FilePath $shortcutInfo.TargetPath
Write-Log " 目标路径: $($shortcutInfo.TargetPath)"
# 检查是否是bat或ps1文件
if ($scriptType -eq 'BAT' -or $scriptType -eq 'PS1') {
$scriptLnkFiles += @{
File = $lnk
Info = $shortcutInfo
Type = $scriptType
}
Write-Log " 识别为 $scriptType 快捷方式"
}
else {
Write-Log " 不是bat/ps1文件,跳过"
}
}
else {
Write-Log " 无法解析快捷方式"
}
}
$totalCount = $batFiles.Count + $ps1Files.Count + $scriptLnkFiles.Count
if ($totalCount -eq 0) {
Write-Log "未找到任何bat/ps1文件或快捷方式"
return
}
Write-Log "总计: $($batFiles.Count) 个bat + $($ps1Files.Count) 个ps1 + $($scriptLnkFiles.Count) 个快捷方式 = $totalCount 个"
Write-Log "========================================="
# 执行每个bat文件
foreach ($batFile in $batFiles) {
Write-Log "正在启动bat文件: $($batFile.Name)"
try {
$process = Start-Process -FilePath $batFile.FullName `
-WorkingDirectory $CurrentPath `
-WindowStyle Normal `
-PassThru
Write-Log " 成功启动 (PID: $($process.Id))"
}
catch {
Write-Log " 启动失败: $_"
}
}
# 执行每个ps1文件
foreach ($ps1File in $ps1Files) {
Write-Log "正在启动ps1文件: $($ps1File.Name)"
try {
$process = Start-Process -FilePath "powershell.exe" `
-ArgumentList "-ExecutionPolicy Bypass -NoProfile -File `"$($ps1File.FullName)`"" `
-WorkingDirectory $CurrentPath `
-WindowStyle Normal `
-PassThru
Write-Log " 成功启动 (PID: $($process.Id))"
}
catch {
Write-Log " 启动失败: $_"
}
}
# 执行每个快捷方式
foreach ($lnkItem in $scriptLnkFiles) {
$lnkFile = $lnkItem.File
$shortcutInfo = $lnkItem.Info
$scriptType = $lnkItem.Type
Write-Log "正在启动快捷方式: $($lnkFile.Name) [$scriptType]"
Write-Log " 目标: $($shortcutInfo.TargetPath)"
try {
# 检查目标文件是否存在
if (-not (Test-Path $shortcutInfo.TargetPath)) {
Write-Log " 错误: 目标文件不存在"
continue
}
# 确定工作目录
$workDir = $shortcutInfo.WorkingDirectory
if ([string]::IsNullOrEmpty($workDir) -or -not (Test-Path $workDir)) {
$workDir = Split-Path $shortcutInfo.TargetPath -Parent
Write-Log " 使用目标文件目录作为工作目录: $workDir"
}
else {
Write-Log " 工作目录: $workDir"
}
# 根据类型启动
if ($scriptType -eq 'BAT') {
# 启动bat文件
$startParams = @{
FilePath = $shortcutInfo.TargetPath
WorkingDirectory = $workDir
WindowStyle = 'Normal'
PassThru = $true
}
# 如果有参数,添加参数
if (-not [string]::IsNullOrEmpty($shortcutInfo.Arguments)) {
$startParams.ArgumentList = $shortcutInfo.Arguments
Write-Log " 参数: $($shortcutInfo.Arguments)"
}
$process = Start-Process @startParams
Write-Log " 成功启动 (PID: $($process.Id))"
}
elseif ($scriptType -eq 'PS1') {
# 启动ps1文件
$psArgs = "-ExecutionPolicy Bypass -NoProfile -File `"$($shortcutInfo.TargetPath)`""
# 如果有参数,添加到命令行
if (-not [string]::IsNullOrEmpty($shortcutInfo.Arguments)) {
$psArgs += " $($shortcutInfo.Arguments)"
Write-Log " 参数: $($shortcutInfo.Arguments)"
}
$process = Start-Process -FilePath "powershell.exe" `
-ArgumentList $psArgs `
-WorkingDirectory $workDir `
-WindowStyle Normal `
-PassThru
Write-Log " 成功启动 (PID: $($process.Id))"
}
}
catch {
Write-Log " 启动失败: $_"
}
}
Write-Log "========================================="
Write-Log "所有脚本文件已启动完成"
}
# 函数: 服务模式运行(持续运行)
function Run-AsService {
try {
Write-Log "=== 服务启动 ==="
Write-Log "PowerShell 版本: $($PSVersionTable.PSVersion)"
Write-Log "当前用户: $env:USERNAME"
Write-Log "工作目录: $CurrentPath"
# 运行所有脚本文件
Run-AllBatchFiles
Write-Log "所有脚本已启动,服务任务完成"
}
catch {
Write-Log "服务运行异常: $_"
Write-Log "堆栈跟踪: $($_.ScriptStackTrace)"
}
}
# 函数: 安装Windows服务(使用任务计划程序)
function Install-Service {
Write-Host "开始安装开机自启任务..." -ForegroundColor Cyan
# 检查管理员权限
if (-NOT ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) {
Write-Host "错误: 需要管理员权限来安装服务" -ForegroundColor Red
Write-Host "请以管理员身份运行PowerShell" -ForegroundColor Yellow
exit 1
}
# 检查任务是否已存在
$existingTask = Get-ScheduledTask -TaskName $ServiceName -ErrorAction SilentlyContinue
if ($existingTask) {
Write-Host "任务已存在,请先卸载" -ForegroundColor Yellow
return
}
# 获取当前脚本的完整路径
$scriptPath = $MyInvocation.PSCommandPath
if ([string]::IsNullOrEmpty($scriptPath)) {
# 如果无法自动获取,尝试在当前目录查找
$possibleNames = @("BatchRunner.ps1", "批量启动.ps1")
foreach ($name in $possibleNames) {
$testPath = Join-Path $CurrentPath $name
if (Test-Path $testPath) {
$scriptPath = $testPath
break
}
}
}
if ([string]::IsNullOrEmpty($scriptPath) -or -not (Test-Path $scriptPath)) {
Write-Host "错误: 无法找到脚本文件" -ForegroundColor Red
Write-Host "请确保脚本文件存在于: $CurrentPath" -ForegroundColor Yellow
return
}
Write-Host "使用任务计划程序安装..." -ForegroundColor Cyan
try {
# 创建任务动作
$action = New-ScheduledTaskAction -Execute "PowerShell.exe" `
-Argument "-ExecutionPolicy Bypass -NoProfile -WindowStyle Hidden -File `"$scriptPath`" -Action service"
# 创建任务触发器(系统启动时,延迟30秒)
$trigger = New-ScheduledTaskTrigger -AtStartup
$trigger.Delay = 'PT30S' # 延迟30秒启动
# 创建任务主体(使用当前用户账户运行)
$principal = New-ScheduledTaskPrincipal -UserId $env:USERNAME -LogonType Interactive -RunLevel Highest
# 创建任务设置
$settings = New-ScheduledTaskSettingsSet `
-AllowStartIfOnBatteries `
-DontStopIfGoingOnBatteries `
-StartWhenAvailable `
-ExecutionTimeLimit (New-TimeSpan -Minutes 10)
# 注册任务
Register-ScheduledTask `
-TaskName $ServiceName `
-Action $action `
-Trigger $trigger `
-Principal $principal `
-Settings $settings `
-Description $ServiceDescription | Out-Null
Write-Host "`n任务创建成功!" -ForegroundColor Green
Write-Host " 任务名称: $ServiceName" -ForegroundColor White
Write-Host " 启动方式: 系统启动时(延迟30秒)" -ForegroundColor White
Write-Host " 运行账户: $env:USERNAME" -ForegroundColor White
Write-Host " 脚本路径: $scriptPath" -ForegroundColor White
Write-Host " 工作目录: $CurrentPath" -ForegroundColor White
Write-Host " 日志文件: $LogFile" -ForegroundColor White
$startNow = Read-Host "`n是否立即运行任务测试? (Y/N)"
if ($startNow -eq 'Y' -or $startNow -eq 'y') {
Start-ScheduledTask -TaskName $ServiceName
Write-Host "任务已启动" -ForegroundColor Green
Start-Sleep -Seconds 2
Write-Host "查看日志: Get-Content '$LogFile' -Tail 20" -ForegroundColor Yellow
}
}
catch {
Write-Host "创建任务失败: $_" -ForegroundColor Red
}
}
# 函数: 卸载服务
function Uninstall-Service {
Write-Host "开始卸载服务..." -ForegroundColor Cyan
# 检查管理员权限
if (-NOT ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) {
Write-Host "错误: 需要管理员权限来卸载服务" -ForegroundColor Red
Write-Host "请以管理员身份运行PowerShell" -ForegroundColor Yellow
exit 1
}
$uninstalled = $false
# 尝试删除任务计划
$task = Get-ScheduledTask -TaskName $ServiceName -ErrorAction SilentlyContinue
if ($task) {
Write-Host "停止并删除任务计划..." -ForegroundColor Yellow
try {
Stop-ScheduledTask -TaskName $ServiceName -ErrorAction SilentlyContinue
Start-Sleep -Seconds 1
}
catch {
# 忽略停止错误
}
Unregister-ScheduledTask -TaskName $ServiceName -Confirm:$false
Write-Host "任务已删除" -ForegroundColor Green
$uninstalled = $true
}
if ($uninstalled) {
Write-Host "`n卸载完成!" -ForegroundColor Green
}
else {
Write-Host "未找到已安装的任务" -ForegroundColor Yellow
}
}
# 函数: 启动服务
function Start-ServiceManual {
if (-NOT ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) {
Write-Host "错误: 需要管理员权限" -ForegroundColor Red
exit 1
}
# 检查任务是否存在
$task = Get-ScheduledTask -TaskName $ServiceName -ErrorAction SilentlyContinue
if ($task) {
Write-Host "启动任务: $ServiceName" -ForegroundColor Cyan
Start-ScheduledTask -TaskName $ServiceName
Write-Host "任务已启动" -ForegroundColor Green
Start-Sleep -Seconds 2
Write-Host "查看日志: Get-Content '$LogFile' -Tail 20" -ForegroundColor Yellow
}
else {
Write-Host "未找到已安装的任务,请先安装" -ForegroundColor Yellow
}
}
# 函数: 停止服务
function Stop-ServiceManual {
if (-NOT ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) {
Write-Host "错误: 需要管理员权限" -ForegroundColor Red
exit 1
}
# 检查任务是否存在
$task = Get-ScheduledTask -TaskName $ServiceName -ErrorAction SilentlyContinue
if ($task) {
Write-Host "停止任务: $ServiceName" -ForegroundColor Cyan
Stop-ScheduledTask -TaskName $ServiceName
Write-Host "任务已停止" -ForegroundColor Green
}
else {
Write-Host "未找到已安装的任务" -ForegroundColor Yellow
}
}
#endregion
# 主程序
Write-Host "========================================" -ForegroundColor Cyan
Write-Host " 批处理文件批量启动工具" -ForegroundColor Cyan
Write-Host "========================================" -ForegroundColor Cyan
Write-Host "工作目录: $CurrentPath" -ForegroundColor White
Write-Host ""
switch ($Action.ToLower()) {
'install' {
Install-Service
}
'uninstall' {
Uninstall-Service
}
'start' {
Start-ServiceManual
}
'stop' {
Stop-ServiceManual
}
'service' {
# 这是服务模式,由任务计划调用
Run-AsService
}
'run' {
Run-AllBatchFiles
}
default {
Write-Host "使用方法:" -ForegroundColor Yellow
Write-Host " .\BatchRunner.ps1 # 直接运行所有脚本" -ForegroundColor White
Write-Host " .\BatchRunner.ps1 -Action install # 安装为开机自启" -ForegroundColor White
Write-Host " .\BatchRunner.ps1 -Action uninstall # 卸载开机自启" -ForegroundColor White
Write-Host " .\BatchRunner.ps1 -Action start # 手动触发任务" -ForegroundColor White
Write-Host " .\BatchRunner.ps1 -Action stop # 停止运行中的任务" -ForegroundColor White
Write-Host ""
Write-Host "支持的文件类型:" -ForegroundColor Yellow
Write-Host " - *.bat # 批处理文件" -ForegroundColor White
Write-Host " - *.ps1 # PowerShell脚本" -ForegroundColor White
Write-Host " - *.lnk # 指向bat/ps1的快捷方式" -ForegroundColor White
Write-Host ""
Write-Host "查看日志:" -ForegroundColor Yellow
Write-Host " Get-Content .\service.log -Tail 20" -ForegroundColor White
}
}