In modern IT, we rarely work in a pure environment. While PowerShell is the superior tool for structured automation, we often need to interact with other shells: Bash on Linux and CMD on Windows. Instead of choosing between them, a true power user masters their interoperability.

This guide covers the essential “bridging” techniques to make these shells work together, built on one central concept.


Part 1: The Core Concept - Objects vs. Text

Understanding interoperability is simple if you remember one rule: PowerShell works with Objects; Bash and CMD work with Text.

  • PowerShell’s Strength: It passes rich, structured objects down the pipeline. You can query properties like $service.Status or $process.CPU directly.
  • Bash/CMD’s Strength: They are expert text parsers, designed to manipulate streams of string data with tools like grep, sed, awk, or findstr.

Every time you cross the boundary between PowerShell and another shell, your data is converted:

  • PowerShell -> Bash/CMD: Rich objects are “melted” into plain text (.ToString()).
  • Bash/CMD -> PowerShell: Text output arrives in PowerShell as an array of strings.

The key to success is to do your heavy data lifting in PowerShell and call out to the other shells only when necessary to run a legacy script or a native command.


Part 2: PowerShell and Bash on Linux

On Linux, Bash is the native tongue. Combining it with PowerShell gives you the best of both worlds: Bash’s text-processing speed and PowerShell’s structured data and API integration.

Calling Bash from PowerShell

PowerShell on Linux treats native binaries just like Windows does. You can run them directly. For complex Bash syntax (loops, redirections), wrap it in bash -c.

# Method A: Direct Execution of a simple command chain
ls -lah /var/log | grep "error"

# Method B: Using bash -c for complex syntax
$targetDir = "/tmp/logs"
# Use single quotes for the bash script to avoid variable collision
bash -c 'for f in '$targetDir'/*.log; do echo "Processing $f"; done'

Calling PowerShell from Bash

This is common in CI/CD pipelines (like GitHub Actions) where the runner defaults to Bash. Use pwsh -c (or pwsh -File) to hand off tasks to PowerShell.

# Method A: Get a structured date from PowerShell
TODAY=$(pwsh -c "Get-Date -Format 'yyyy-MM-dd'")
echo "Today is $TODAY"

# Method B: Pass a Bash variable into PowerShell
MY_USER="deploy-bot"
pwsh -c "Write-Host 'Deploying as user: $MY_USER'"

Handling the Bash -> PowerShell Data Boundary

When Bash pipes text to PowerShell, you receive an array of strings. To make it useful, you often need to parse it back into objects.

# 'ps aux' returns text lines. We use a regex to parse it.
bash -c "ps aux" | ForEach-Object {
    if ($_ -match "(\S+)\s+(\d+)") {
        [PSCustomObject]@{
            User = $matches[1]
            PID  = [int]$matches[2]
        }
    }
}

Handling the PowerShell -> Bash Data Boundary

To avoid PowerShell outputting System.Object[], explicitly convert your data to a string format like JSON before piping to a Bash tool like jq.

# Generate JSON in PowerShell, process it with jq in Bash
pwsh -c "Get-Process | Select-Object Name, Id | ConvertTo-Json" | jq '.[0]'

Part 3: PowerShell and CMD on Windows

cmd.exe persists for legacy compatibility. Mastering its interoperability is key to modernizing old automation without starting from scratch.

Calling CMD from PowerShell

For most external commands (ping.exe, ipconfig.exe), PowerShell can call them directly. For internal CMD commands (dir, mklink) or batch files, use cmd /c.

# Method A: Direct Execution
ipconfig /all

# Method B: Explicit call for internal commands or batch files
cmd /c "echo %USERNAME% && dir C:\ && C:\LegacyScripts\OldBackup.bat"

Calling PowerShell from CMD

This is essential for upgrading legacy batch files. Use powershell -Command or pwsh -File.

@echo off
echo Checking disk space from a legacy batch file...
powershell -Command "Get-PSDrive C | Format-Table Free, Used"

echo Running a modern script and passing a variable...
SET LOGFILE=C:\logs\backup.log
:: Pass variables from CMD to PowerShell
pwsh -File "C:\Scripts\ModernBackup.ps1" -LogPath "%LOGFILE%"

Capturing PowerShell Output in CMD

Capturing PowerShell output back into a CMD variable requires a for /f loop.

for /f "delims=" %%a in ('powershell -c "Get-Date -Format yyyyMMdd"') do set TODAY=%%a
echo Today is %TODAY%

Conclusion

Don’t choose between shells—use them together. The interoperability pattern is the same for both Bash and CMD:

  • Call out from PowerShell using bash -c '...' or cmd /c '...'.
  • Call in to PowerShell using pwsh -c "..." or pwsh -File "...".
  • Manage the boundary: Always be aware of when your structured Objects are being converted to simple Text. Perform complex logic in PowerShell whenever possible.

By mastering these simple bridging techniques, you can leverage the best tool for the job, whether it’s a 20-year-old batch file or a modern PowerShell module.