Cross-platform scripts
Cross-platform scripts
When a project targets users on Windows, macOS, and Linux all at once, the choice of tool for automation scripts becomes a recurring question. There is no single right answer — environment constraints and team familiarity steer the pick.
1. bash everywhere + Git Bash · WSL guidance
Write all automation in bash and tell Windows users to run it inside Git Bash (bundled with Git for Windows) or WSL2.
- Pros — most natural on macOS · Linux. Plenty of online material. Standard shell tools (
grep,awk,sed) are right there. - Cons — extra installs for native Windows users. Path mismatches (
/c/Users/...vsC:\Users\...). Won't run directly under cmd or PowerShell. - Good fit — backend, data, and DevOps-centric teams.
2. Node-based scripts
Write automation on top of Node.js. JavaScript/TypeScript front-end projects already have Node, so there is little extra dependency.
Notable tools:
zx(Google) — a Node library for running commands with a bash-like feel. Syntax such asawait $\ls``.tsx— runs TypeScript files directly.tsx scripts/build.ts.- Plain
.mjs— no external deps, just ESM.node scripts/clean.mjs.
// scripts/clean.mjs
import { rm } from "node:fs/promises";
await rm("dist", { recursive: true, force: true });
console.log("cleaned");
The strength is OS abstraction in the standard library (fs, path, os). The downside is that without Node nothing runs, and non-CLI work can balloon in code volume.
3. PowerShell 7+
PowerShell 7+ runs on Windows, macOS, and Linux. Object pipes and the .NET library underneath.
- Pros — most natural for Windows users. Rich admin/automation cmdlets. 7+ is genuinely cross-platform.
- Cons — a separate install for macOS and Linux users (
brew install powershell,apt install powershell). The shell itself has lower mindshare in the Unix camp. - Good fit — Windows-led teams adding macOS · Linux compatibility.
4. Python scripts
Python ships a thick standard library with solid OS abstractions (pathlib, subprocess, shutil).
- Pros — broad standard library. Readable. Data, HTTP, file handling each fit on a single line.
- Cons — needs the interpreter and version management (virtual envs). For simple command launching, the code is longer than a shell line.
- Good fit — projects already centered on Python, or scripts dealing with data and networking.
5. Make · Just
make is the classic Unix build automation tool. just is a modern simplification of the same idea.
make— bundled on macOS · Linux. Windows needs a separate install (MSYS2, GnuWin). Makefile tab/space syntax is finicky.just— written in Rust. A flat command collection without dependency graphs. Friendlier to cross-platform thanmake.cargo install justorbrew install just.
# Makefile
build:
npm run build
test:
npm test
# justfile
build:
npm run build
test:
npm test
The strength is gathering commands in one file. The downside is that internally each rule still calls a shell command, so OS differences come back.
6. package.json scripts
For JS · TS projects, package.json's scripts is the de facto entry point.
{
"scripts": {
"dev": "next dev",
"build": "next build",
"lint": "eslint ."
}
}
Works under npm, pnpm, or yarn. Calls tools from node_modules/.bin without global installs. OS-sensitive commands get smoothed with helpers like cross-env (env vars), rimraf (directory removal), or npm-run-all.
Once a scripts entry grows long, splitting it out into zx or a separate .mjs becomes friendlier to maintenance.
7. Comparison summary
| Approach | Windows fit | macOS · Linux fit | Extra install | Suitable scale |
|---|---|---|---|---|
| bash | Moderate (Git Bash needed) | High | Git Bash · WSL | Any size |
Node (zx, .mjs) |
High | High | Node | Small to medium |
| PowerShell 7 | High | Moderate | pwsh | Small to medium |
| Python | High | High | Python | Medium to large |
| Make | Moderate | High | (separate on Windows) | Small to medium |
| Just | High | High | just binary | Small to medium |
8. The same task in four flavors
# bash
rm -rf dist
# PowerShell
Remove-Item -Recurse -Force dist -ErrorAction SilentlyContinue
// Node .mjs
import { rm } from "node:fs/promises";
await rm("dist", { recursive: true, force: true });
# Python
import shutil; shutil.rmtree("dist", ignore_errors=True)
9. Common pitfalls
bash scripts saved with CRLF break on Linux — declare *.sh text eol=lf in .gitattributes.
Node scripts hard-coding "a/b/c" instead of path.join — works on Windows but logs and outputs look off.
Calling pwsh assuming PowerShell 7+ is installed when only 5.1 is — detect first and guide.
Makefiles indented with spaces breaking make — Make requires literal tabs.
Calling && from PowerShell 5.1 (instead of cmd) on Windows — parser error.
Closing thoughts
There is no single right answer for cross-platform scripts. The team's environment mix (Windows · Unix) and the project's nature (front-end · backend · data) decide. Either keeping three OS-equivalent scripts side by side or unifying on a Node .mjs set are the two shapes that show up most often.
Next
- markdown
- text-encoding-line-endings
google/zx · tsx · PowerShell install · casey/just · npm Docs scripts for reference.