PowerShell Where-Object Fails from Bash? It Is Usually Variable Expansion
PowerShell commands that work in PowerShell can fail when you launch them through Bash.
The failure is especially confusing with Where-Object, because the command looks correct:
powershell -Command "Get-Process | Where-Object { $_.Id -eq $target_pid }"
The problem is that Bash sees $target_pid and $_ before PowerShell ever receives the command. By the time PowerShell runs, the script block may already be corrupted.
The symptom
You may see:
- PowerShell parser errors
- empty values inside
Where-Object - filters that return nothing even though the process exists
- errors mentioning unexpected
\or missing expressions
The command works when typed directly in PowerShell, but fails when wrapped in bash, sh, Git Bash, or a tool that launches commands through a POSIX shell.
Root cause
There are two shells involved:
Bash parses the command string first
PowerShell receives the already-expanded result
In this string:
powershell -Command "Get-Process | Where-Object { $_.Id -eq $target_pid }"
Bash tries to expand $target_pid. It may also treat $_ as a shell variable. If those variables are not defined in Bash, they become empty.
PowerShell then receives something like:
Get-Process | Where-Object { .Id -eq }
That is not the command you thought you sent.
Do not fix it with backslashes
This usually makes things worse:
powershell -Command "Get-Process | Where-Object { \$_.Id -eq \$target_pid }"
Backslash escaping is a Bash convention. PowerShell does not treat \$ as a normal escaped dollar in a script block. It may receive a literal backslash and then fail parsing.
Best fix: keep the logic inside PowerShell
If the logic can be self-contained, write it entirely as PowerShell:
powershell -Command "if (Get-Process -Name node -ErrorAction SilentlyContinue) { Write-Output 'running' } else { Write-Output 'stopped' }"
No Bash variables cross into the PowerShell string, so there is less to corrupt.
If a value must cross, use parameters
When you must pass a value from Bash to PowerShell, pass it as an argument:
target_pid=1234
powershell -Command "param([int]$pid) if (Get-Process -Id $pid -ErrorAction SilentlyContinue) { Write-Output 'found' }" -ArgumentList "$target_pid"
Now Bash passes one argument, and PowerShell receives it through param().
Practical checklist
When a PowerShell command fails only when launched from Bash:
- look for
$inside the-Commandstring - avoid
Where-Object { $_ ... }unless quoting is fully controlled - prefer pure PowerShell logic
- pass values through
param()and-ArgumentList - test the PowerShell body directly in PowerShell first
Summary
This is not a PowerShell bug. It is a shell boundary bug.
Bash expands $... before PowerShell gets the command. If you need reliable automation, either keep the logic entirely inside PowerShell or pass values through -ArgumentList.