How do I restart a batch file in PowerShell?

60 Views Asked by At

Here is what I have tried:

Get-Process | Where-Object {$_.Path -eq "C:\path\stream1.bat"} | Stop-Process -Force

Start-Sleep -Seconds 5

Start-Process -FilePath "cmd.exe" -ArgumentList "/c `"C:\path\stream1.bat`""

The Start-Process command works perfectly, however the Stop-Process command does nothing.

2

There are 2 best solutions below

0
Santiago Squarzon On BEST ANSWER

One issue with your current code is that .Path (in reality this property is .MainModule.FileName) will be the path to cmd.exe not the .bat file itself. What you can do in this case is query Win32_Process and filter by the CommandLine property where it contains the path of your .bat. This solves one of the problems.

The next problem will be to find all sub processes that your .bat file could've started, these need to be terminated first before terminating cmd.exe itself. For this, again, you can query Win32_Process to find all processes having the ParentProcessId equal to the process id of your cmd.exe process.

$bat = 'C:\path\stream1.bat'

# WQL requires escaping of `\`
$cmdline = $bat.Replace('\', '\\')

$getCimInstanceSplat = @{
    Filter    = "Name = 'cmd.exe' AND CommandLine LIKE '%$cmdline%'"
    ClassName = 'Win32_Process'
}
# Find the cmd process containing the path of the `.bat` file
$proc = Get-CimInstance @getCimInstanceSplat

$getCimInstanceSplat = @{
    Filter    = "ParentProcessId = '$($proc.ProcessId)'"
    ClassName = 'Win32_Process'
}
# Find all processes started by that bat file
Get-CimInstance @getCimInstanceSplat |
    # and terminate all of them
    Invoke-CimMethod -MethodName Terminate

# In case the parent process didn't terminate after that
# attempt to terminate it as well
$proc | Invoke-CimMethod -MethodName Terminate -EA 0
0
mklement0 On

To complement Santiago's helpful answer with a PowerShell (Core) 7 solution:

Get-Process -ErrorAction Ignore cmd | 
  Where CommandLine -match ('\b{0}\b' -f [regex]::Escape('C:\path\stream1.bat')) | 
  ForEach Kill $true
  • In PowerShell 7, the objects output by Get-Process have a .CommandLine property, which reflects the batch-file path (typically implicitly) passed to cmd.exe for execution of a batch file.

  • Any matching process(es), along with their child processes, are then forcefully terminated via the .Kill() method, with a $true argument passed to the entireProcessTree parameter, using ForEach-Object (one of whose built-in aliases is ForEach) with simplified syntax.

    • Note that, as of PowerShell 7.4.x, use of Stop-Process is not an option, because it only forcefully terminates the target process alone, not also its descendants (process tree).
      GitHub issue #15075 is a green-lit proposal to add a -Recurse / -IncludeChildProcess switch to Stop-Process to overcome this limitation.