home / skills / josiahsiegel / claude-plugin-marketplace / powershell-shell-detection

powershell-shell-detection skill

/plugins/powershell-master/skills/powershell-shell-detection

npx playbooks add skill josiahsiegel/claude-plugin-marketplace --skill powershell-shell-detection

Review the files below or copy the command above to add this skill to your agents.

Files (1)
SKILL.md
12.5 KB
---
name: powershell-shell-detection
description: Shell detection and cross-shell compatibility guidance for PowerShell vs Git Bash/MSYS2 on Windows
---

# PowerShell Shell Detection & Cross-Shell Compatibility

Critical guidance for distinguishing between PowerShell and Git Bash/MSYS2 shells on Windows, with shell-specific path handling and compatibility notes.

## Shell Detection Priority (Windows)

When working on Windows, correctly identifying the shell environment is crucial for proper path handling and command execution.

### Detection Order (Most Reliable First)

1. **process.env.PSModulePath** (PowerShell specific)
2. **process.env.MSYSTEM** (Git Bash/MinGW specific)
3. **process.env.WSL_DISTRO_NAME** (WSL specific)
4. **uname -s output** (Cross-shell, requires execution)

## PowerShell Detection

### Primary Indicators

**PSModulePath (Most Reliable):**
```powershell
# PowerShell detection
if ($env:PSModulePath) {
    Write-Host "Running in PowerShell"
    # PSModulePath contains 3+ paths separated by semicolons
    $env:PSModulePath -split ';'
}

# Check PowerShell version
$PSVersionTable.PSVersion
# Output: 7.5.4 (PowerShell 7) or 5.1.x (Windows PowerShell)
```

**PowerShell-Specific Variables:**
```powershell
# These only exist in PowerShell
$PSVersionTable    # Version info
$PSScriptRoot      # Script directory
$PSCommandPath     # Script full path
$IsWindows         # Platform detection (PS 7+)
$IsLinux           # Platform detection (PS 7+)
$IsMacOS           # Platform detection (PS 7+)
```

### Shell Type Detection in Scripts

```powershell
function Get-ShellType {
    if ($PSVersionTable) {
        return "PowerShell $($PSVersionTable.PSVersion)"
    }
    elseif ($env:PSModulePath -and ($env:PSModulePath -split ';').Count -ge 3) {
        return "PowerShell (detected via PSModulePath)"
    }
    else {
        return "Not PowerShell"
    }
}

Get-ShellType
```

## Git Bash / MSYS2 Detection

### Primary Indicators

**MSYSTEM Environment Variable (Most Reliable):**
```bash
# Bash detection in Git Bash/MSYS2
if [ -n "$MSYSTEM" ]; then
    echo "Running in Git Bash/MSYS2: $MSYSTEM"
fi

# MSYSTEM values:
# MINGW64 - Native Windows 64-bit environment
# MINGW32 - Native Windows 32-bit environment
# MSYS    - POSIX-compliant build environment
```

**Secondary Detection Methods:**
```bash
# Using OSTYPE (Bash-specific)
case "$OSTYPE" in
    msys*)   echo "MSYS/Git Bash" ;;
    cygwin*) echo "Cygwin" ;;
    linux*)  echo "Linux" ;;
    darwin*) echo "macOS" ;;
esac

# Using uname (Most portable)
case "$(uname -s)" in
    MINGW64*) echo "Git Bash 64-bit" ;;
    MINGW32*) echo "Git Bash 32-bit" ;;
    MSYS*)    echo "MSYS" ;;
    CYGWIN*)  echo "Cygwin" ;;
    Linux*)   echo "Linux" ;;
    Darwin*)  echo "macOS" ;;
esac
```

## Cross-Shell Compatibility on Windows

### Critical Differences

| Aspect | PowerShell | Git Bash/MSYS2 |
|--------|-----------|----------------|
| **Environment Variable** | `$env:VARIABLE` | `$VARIABLE` |
| **Path Separator** | `;` (semicolon) | `:` (colon) |
| **Path Style** | `C:\Windows\System32` | `/c/Windows/System32` |
| **Home Directory** | `$env:USERPROFILE` | `$HOME` |
| **Temp Directory** | `$env:TEMP` | `/tmp` |
| **Command Format** | `Get-ChildItem` | `ls` (native command) |
| **Aliases** | PowerShell cmdlet aliases | Unix command aliases |

### Path Handling: PowerShell vs Git Bash

**PowerShell Path Handling:**
```powershell
# Native Windows paths work directly
$path = "C:\Users\John\Documents"
Test-Path $path  # True

# Forward slashes also work in PowerShell 7+
$path = "C:/Users/John/Documents"
Test-Path $path  # True

# Use Join-Path for cross-platform compatibility
$configPath = Join-Path -Path $PSScriptRoot -ChildPath "config.json"

# Use [System.IO.Path] for advanced scenarios
$fullPath = [System.IO.Path]::Combine($home, "documents", "file.txt")
```

**Git Bash Path Handling:**
```bash
# Git Bash uses Unix-style paths
path="/c/Users/John/Documents"
test -d "$path" && echo "Directory exists"

# Automatic path conversion (CAUTION)
# Git Bash converts Unix-style paths to Windows-style
# /c/Users → C:\Users (automatic)
# Arguments starting with / may be converted unexpectedly

# Use cygpath for manual conversion
cygpath -u "C:\path"      # → /c/path (Unix format)
cygpath -w "/c/path"      # → C:\path (Windows format)
cygpath -m "/c/path"      # → C:/path (Mixed format)
```

## Automatic Path Conversion in Git Bash (CRITICAL)

Git Bash/MSYS2 automatically converts paths in certain scenarios, which can cause issues:

### What Triggers Conversion

```bash
# Leading forward slash triggers conversion
command /foo         # Converts to C:\msys64\foo

# Path lists with colons
export PATH=/foo:/bar  # Converts to C:\msys64\foo;C:\msys64\bar

# Arguments after dashes
command --path=/foo    # Converts to --path=C:\msys64\foo
```

### What's Exempt from Conversion

```bash
# Arguments with equals sign (variable assignments)
VAR=/foo command      # NOT converted

# Drive specifiers
command C:/path       # NOT converted

# Arguments with semicolons (already Windows format)
command "C:\foo;D:\bar"  # NOT converted

# Double slashes (Windows switches)
command //e //s       # NOT converted
```

### Disabling Path Conversion

```bash
# Disable ALL conversion (Git Bash)
export MSYS_NO_PATHCONV=1
command /foo  # Stays as /foo

# Exclude specific patterns (MSYS2)
export MSYS2_ARG_CONV_EXCL="*"           # Exclude everything
export MSYS2_ARG_CONV_EXCL="--dir=;/test"  # Specific prefixes
```

## When to Use PowerShell vs Git Bash on Windows

### Use PowerShell When:

- ✅ **Windows-specific tasks** - Registry, WMI, Windows services
- ✅ **Azure/Microsoft 365 automation** - Az, Microsoft.Graph modules
- ✅ **Module ecosystem** - Leverage PSGallery modules
- ✅ **Object-oriented pipelines** - Rich object manipulation
- ✅ **Native Windows integration** - Built into Windows
- ✅ **CI/CD with pwsh** - GitHub Actions, Azure DevOps
- ✅ **Cross-platform scripting** - PowerShell 7 works on Linux/macOS

**Example PowerShell Scenario:**
```powershell
# Azure VM management with Az module
Connect-AzAccount
Get-AzVM -ResourceGroupName "Production" |
    Where-Object {$_.PowerState -eq "VM running"} |
    Stop-AzVM -Force
```

### Use Git Bash When:

- ✅ **Unix tool compatibility** - sed, awk, grep, find
- ✅ **Git operations** - Native Git command-line experience
- ✅ **POSIX script execution** - Running Linux shell scripts
- ✅ **Cross-platform shell scripts** - Bash scripts from Linux/macOS
- ✅ **Text processing** - Unix text utilities (sed, awk, cut)
- ✅ **Development workflows** - Node.js, Python, Ruby with Unix tools

**Example Git Bash Scenario:**
```bash
# Git workflow with Unix tools
git log --oneline | grep -i "feature" | awk '{print $1}' |
    xargs git show --stat
```

## Shell-Aware Script Design

### Detect and Adapt (PowerShell)

```powershell
# Detect if running in PowerShell or Git Bash context
function Test-PowerShellContext {
    return ($null -ne $PSVersionTable)
}

# Adapt path handling based on context
function Get-CrossPlatformPath {
    param([string]$Path)

    if (Test-PowerShellContext) {
        # PowerShell: Use Join-Path
        return (Resolve-Path $Path -ErrorAction SilentlyContinue).Path
    }
    else {
        # Non-PowerShell context
        Write-Warning "Not running in PowerShell. Path operations may differ."
        return $Path
    }
}
```

### Detect and Adapt (Bash)

```bash
# Detect shell environment
detect_shell() {
    if [ -n "$MSYSTEM" ]; then
        echo "git-bash"
    elif [ -n "$PSModulePath" ]; then
        echo "powershell"
    elif [ -n "$WSL_DISTRO_NAME" ]; then
        echo "wsl"
    else
        echo "unix"
    fi
}

# Adapt path handling
convert_path() {
    local path="$1"
    local shell_type=$(detect_shell)

    case "$shell_type" in
        git-bash)
            # Convert Windows path to Unix style
            echo "$path" | sed 's|\\|/|g' | sed 's|^\([A-Z]\):|/\L\1|'
            ;;
        *)
            echo "$path"
            ;;
    esac
}

# Usage
shell_type=$(detect_shell)
echo "Running in: $shell_type"
```

## Environment Variable Comparison

### Common Environment Variables

| Variable | PowerShell | Git Bash | Purpose |
|----------|-----------|----------|---------|
| **Username** | `$env:USERNAME` | `$USER` | Current user |
| **Home Directory** | `$env:USERPROFILE` | `$HOME` | User home |
| **Temp Directory** | `$env:TEMP` | `/tmp` | Temporary files |
| **Path List** | `$env:Path` (`;` sep) | `$PATH` (`:` sep) | Executable paths |
| **Shell Detection** | `$env:PSModulePath` | `$MSYSTEM` | Shell identifier |

### Cross-Shell Variable Access

**PowerShell accessing environment variables:**
```powershell
$env:PATH              # Current PATH
$env:PSModulePath      # PowerShell module paths
$env:MSYSTEM           # Would be empty in PowerShell
[Environment]::GetEnvironmentVariable("PATH", "Machine")  # System PATH
```

**Git Bash accessing environment variables:**
```bash
echo $PATH             # Current PATH
echo $MSYSTEM          # Git Bash: MINGW64, MINGW32, or MSYS
echo $PSModulePath     # Would be empty in pure Bash
```

## Practical Examples

### Example 1: Cross-Shell File Finding

**PowerShell:**
```powershell
# Find files modified in last 7 days
Get-ChildItem -Path "C:\Projects" -Recurse -File |
    Where-Object { $_.LastWriteTime -gt (Get-Date).AddDays(-7) } |
    Select-Object FullName, LastWriteTime
```

**Git Bash:**
```bash
# Same operation in Git Bash
find /c/Projects -type f -mtime -7 -exec ls -lh {} \;
```

### Example 2: Process Management

**PowerShell:**
```powershell
# Stop all Chrome processes
Get-Process chrome -ErrorAction SilentlyContinue | Stop-Process -Force
```

**Git Bash:**
```bash
# Same operation in Git Bash
ps aux | grep chrome | awk '{print $2}' | xargs kill -9 2>/dev/null
```

### Example 3: Text File Processing

**PowerShell:**
```powershell
# Extract unique email addresses from logs
Get-Content "logs.txt" |
    Select-String -Pattern '\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b' |
    ForEach-Object { $_.Matches.Value } |
    Sort-Object -Unique
```

**Git Bash:**
```bash
# Same operation in Git Bash
grep -oE '\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b' logs.txt |
    sort -u
```

## Troubleshooting Cross-Shell Issues

### Issue 1: Command Not Found

**Problem:** Command works in one shell but not another
```powershell
# PowerShell
Get-Process  # Works
```
```bash
# Git Bash
Get-Process  # Command not found
```

**Solution:** Understand that PowerShell cmdlets don't exist in Bash. Use native commands or install PowerShell Core (pwsh) in Git Bash:
```bash
# Run PowerShell from Git Bash
pwsh -Command "Get-Process"
```

### Issue 2: Path Format Mismatches

**Problem:** Paths don't work across shells
```bash
# Git Bash path
/c/Users/John/file.txt  # Works in Bash

# PowerShell
Test-Path "/c/Users/John/file.txt"  # May fail
```

**Solution:** Use cygpath for conversion or normalize paths:
```bash
# Convert to Windows format for PowerShell
win_path=$(cygpath -w "/c/Users/John/file.txt")
pwsh -Command "Test-Path '$win_path'"
```

### Issue 3: Alias Conflicts

**Problem:** `ls`, `cd`, `cat` behave differently
```powershell
# PowerShell
ls  # Actually runs Get-ChildItem
```
```bash
# Git Bash
ls  # Runs native Unix ls command
```

**Solution:** Use full cmdlet names in PowerShell scripts:
```powershell
# Instead of: ls
Get-ChildItem  # Explicit cmdlet name
```

## Best Practices Summary

### PowerShell Scripts
1. ✅ Use `$PSScriptRoot` for script-relative paths
2. ✅ Use `Join-Path` or `[IO.Path]::Combine()` for paths
3. ✅ Avoid hardcoded backslashes
4. ✅ Use full cmdlet names (no aliases)
5. ✅ Test on all target platforms
6. ✅ Use `$IsWindows`, `$IsLinux`, `$IsMacOS` for platform detection

### Git Bash Scripts
1. ✅ Check `$MSYSTEM` for Git Bash detection
2. ✅ Use `cygpath` for path conversion when needed
3. ✅ Set `MSYS_NO_PATHCONV=1` to disable auto-conversion if needed
4. ✅ Quote paths with spaces
5. ✅ Use Unix-style paths (`/c/...`) within Bash
6. ✅ Convert to Windows paths when calling Windows tools

### Cross-Shell Development
1. ✅ Document which shell your script requires
2. ✅ Add shell detection at script start
3. ✅ Provide clear error messages for wrong shell
4. ✅ Consider creating wrapper scripts for cross-shell compatibility
5. ✅ Test in both PowerShell and Git Bash if supporting both

## Resources

- [PowerShell Documentation](https://learn.microsoft.com/powershell)
- [Git for Windows Documentation](https://git-scm.com/doc)
- [MSYS2 Documentation](https://www.msys2.org/docs/what-is-msys2/)
- [Cygpath Documentation](https://www.cygwin.com/cygwin-ug-net/cygpath.html)

---

**Last Updated:** October 2025