# Cross Platform Scripting

> Detect the host OS (Windows/macOS/Linux) and PowerShell version (5.1 Desktop vs 7+ Core) before writing or editing scripts, and write/adapt .ps1, .sh, and .cmd scripts to match what's actually available — avoiding PS7-only syntax on PS5.1, GNU-only flags on macOS/BSD tools, and Windows-only assumptions on POSIX shells. Use before writing a new script, when a script fails with a syntax/parameter error that looks version- or OS-specific, or when asked to make a script cross-platform.

- Skill: `sergeyitaly/cross-platform-scripting` (Agent Skill)
- Install (CLI): `npx skillmds@latest add sergeyitaly/cross-platform-scripting`
- Raw SKILL.md: https://api.skillmd.com/api/skills/sergeyitaly/cross-platform-scripting/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: sergeyitaly (https://skillmd.com/u/sergeyitaly)
- Updated: 2026-09-22
- Page: https://skillmd.com/skills/sergeyitaly/cross-platform-scripting

---


# Cross-Platform Scripting

Detect the actual runtime before assuming what syntax/tools are available —
don't write PS7 syntax for a PS5.1 host, or GNU `sed` flags for macOS.

## 1. Detect the OS

- **PowerShell 6+/7+**: `$IsWindows`, `$IsMacOS`, `$IsLinux` (booleans).
- **PowerShell 5.1**: these variables don't exist — PS5.1 only runs on
  Windows, so its mere presence (`$PSVersionTable.PSEdition -eq 'Desktop'`)
  implies Windows.
- **From a POSIX shell**: `uname -s` → `Linux`, `Darwin` (macOS), or
  `MINGW*`/`MSYS*`/`CYGWIN*` (Git Bash / WSL-adjacent on Windows).
- If a project ships parallel scripts (`setup.ps1` + `setup.sh`, or
  `*.cmd` + `*.ps1`), that's a signal contributors use multiple OSes —
  keep both in sync when changing behavior.

## 2. Detect PowerShell version before writing/editing .ps1

```powershell
$PSVersionTable.PSVersion          # e.g. 5.1.19041.x or 7.4.x
$PSVersionTable.PSEdition           # "Desktop" (5.1) or "Core" (7+)
```

Check the project's existing scripts for a stated minimum (a comment like
"PS5-compatible" or `#Requires -Version 7.0`) — match that, don't assume
the latest syntax is fine.

## 3. PS7+-only syntax to avoid on PS5.1

| Feature | PS7+ | PS5.1-safe equivalent |
|---|---|---|
| Pipeline chain operators | `cmd1 && cmd2`, `cmd1 \|\| cmd2` | `cmd1; if ($?) { cmd2 }` |
| Ternary | `$x ? $a : $b` | `if ($x) { $a } else { $b }` |
| Null-coalescing | `$a ?? $b`, `$a ??= $b` | `if ($null -eq $a) { $b } else { $a }` |
| `ForEach-Object -Parallel` | available | sequential `foreach`/`ForEach-Object`, or `Start-Job`/runspaces |
| `Get-Error` | available | inspect `$Error[0]` / `$_.Exception` |
| `&&`/`\|\|` for native exe stderr capture | works as expected | redirecting a native command's stderr inside PS5.1 wraps it as a `NativeCommandError` and can flip `$?` to `$false` even on exit 0 — avoid `2>&1` on native exes; capture stdout/stderr separately if needed |

When a script must support both, default to **PS5.1-safe syntax** and add
`#Requires -Version 7.0` only if a PS7+-only feature is genuinely needed —
then document that requirement at the top of the script and in any README
that lists prerequisites.

## 4. Cross-OS path and tool differences

- Use `Join-Path` (PowerShell) or path-library functions instead of
  hardcoded `\` or `/` separators.
- `sed -i` differs: GNU `sed -i 's/x/y/' file` vs BSD/macOS
  `sed -i '' 's/x/y/' file` (macOS requires the empty extension argument).
  Prefer `perl -i -pe` or a small Python/Node one-liner when a script must
  run on both.
- Line endings: scripts checked out on Windows may get CRLF; `.sh` scripts
  with CRLF line endings fail with `bad interpreter` on Linux/macOS — check
  `.gitattributes` for `* text=auto` or explicit `eol=lf` rules on shell
  scripts.
- `Test-Path`/`New-Item -ItemType Directory -Force` (PowerShell) vs
  `[ -d "$dir" ]`/`mkdir -p` (POSIX) — don't mix syntaxes within one script.

## 5. When generating a new script

1. Detect (or ask) which environment(s) it must run on.
2. If the project already has a script for the same task in another shell
   (`.ps1` next to `.sh`), match its behavior and keep both updated.
3. Default to the most restrictive compatible syntax (PS5.1 / POSIX sh)
   unless the task genuinely needs a newer feature — then state the
   requirement explicitly (`#Requires -Version 7.0`, `#!/usr/bin/env bash`
   with a documented bash-version minimum).
4. Test the script (or at least syntax-check it: `pwsh -NoProfile -Command
   "& { . ./script.ps1 -WhatIf }"` style dry runs, or `bash -n script.sh`)
   before declaring it done.

