Home > database >  powershell function to kill child processes on *nix without Get-CimInstance
powershell function to kill child processes on *nix without Get-CimInstance

Time:12-21

I have a Powershell function KillChildren that works on Windows, but not in bash (on my Mac). The problem is that it uses the windows-only Get-CimInstance cmdlet.

Here is the windows-centric version (whose authorship is unknown to me):

function KillChildren {
    Param (
        [Parameter(Position = 0, Mandatory, ValueFromPipeline)]
        [int[]]$ProcessId,

        [switch]$PassThru
    )

    process {
        foreach ($p in $ProcessId) {
            Get-CimInstance -Class Win32_Process -Filter "ParentProcessId = '$p' AND NOT Name LIKE 'conhost%'" -Property ProcessId |
                Select-Object -ExpandProperty ProcessId |
                KillChildren -PassThru |
                ForEach-Object {
                    Stop-Process -Id $_ -ErrorAction Ignore -Force
                }

            if ($PassThru -and $PassThru.IsPresent) {
                $p
            }
        }
    }
}

How would you write a version of this function that would work in Unix?

The Unix logic-path can't use Get-CimInstance, because that's a windows-specific cmdlet.

This is in an multi-platform repo, and I'm hoping to maintain only a single set of PS scripts, instead of parallel PS and batch scripts.

CodePudding user response:

The following should work on Unix-like platforms to get a given process' child processes (with the given process identified via its PID (process ID), $p) :

Get-Process | Where-Object { $_.Parent.Id -eq $p }

It also works on Windows in principle, assuming you're running PowerShell (Core) 7 rather than Windows PowerShell (where the Get-Process output objects have no .Parent property), but there's a pitfall (which your Get-CimInstance solution already (mostly) accounts for - see next section):

  • If the process whose child processes to kill is the current process and you're running in a regular console window (conhost.exe), you must exclude the automatically created conhost child process from the results, as killing it too would close the terminal window and therefore kill the target process as well.

  • This is not a concern in Windows Terminal.


The following is a cross-edition, cross-platform solution:

# Assumes that $p contains the PID (process ID) of interest.
$(if ($env:OS -eq 'Windows_NT') {
  (Get-CimInstance -Class Win32_Process -Filter "ParentProcessId = $p AND Name != 'conhost.exe'" -Property ProcessId).ProcessId
} else {
  (Get-Process | Where-Object { $_.Parent.Id -eq $p }).Id
}) # | ...

Note:

  • At least hypothetically, eliminating all conhost.exe child processes isn't fully robust, because it bears the risk of false positives (excluding child processes that shouldn't be excluded), given that it's possible to explicitly launch conhost.exe child processes with commands such as conhost cmd /k.
  • Related