Skip to content

Cross-Platform Guide

Ps-bash targets identical command syntax across all three platforms. Under the hood, each platform uses different OS primitives. This page documents where behavior diverges and how ps-bash bridges the gaps.


The ls -l and stat commands display Unix-style permission strings (rwxr-xr-x) on every platform, but the source of truth differs.

Get-BashFileInfo reads the real POSIX mode via .NET’s UnixFileMode property and converts it to a permission string with ConvertTo-PermissionString:

Terminal window
$mode = [int]$Item.UnixFileMode # real kernel mode bits
$permString = ConvertTo-PermissionString -Mode $mode # e.g. "rwxr-xr-x"

Owner, group, and hard-link count come from a native stat call:

Terminal window
# Linux
& /usr/bin/stat -c '%h %U %G' $Item.FullName
# macOS (BSD stat)
& /usr/bin/stat -f '%l %Su %Sg' $Item.FullName

The ps command returns identical column names (PID, User, TTY, Stat, Command, etc.) on all platforms, but the data sources differ significantly.

Get-LinuxProcEntry reads the /proc filesystem directly:

DataSource
PID, PPID, state, CPU ticks/proc/[pid]/stat
UID (resolved to username)/proc/[pid]/status + /usr/bin/getent passwd
Command line/proc/[pid]/cmdline (null-byte separated)
Memory/proc/meminfo for total, RSS from stat
TTYDecoded from tty_nr (major/minor device numbers)

TTY values are real device names like pts/0 or tty1. The Stat column reflects the kernel process state character (S, R, D, Z, etc.).

ColumnLinuxmacOSWindows
PID/proc.NET.NET
PPID/proc/bin/psWMI
User/proc + getent/bin/psWMI or $env:USERNAME
TTYdevice decode/bin/pssession ID
Statkernel stateinferredinferred
VSZ / RSS/proc.NET.NET
Command/proc/cmdlineprocess nameWMI CommandLine

Symbolic and hard links work without restrictions:

Terminal window
ln -s target linkname # symbolic link
ln target linkname # hard link
ln -sf target linkname # force-replace existing

Ps-bash calls native executables by absolute path on Unix platforms to avoid alias recursion.

When ps-bash needs native OS data it cannot get through .NET alone, it calls the real binary directly:

CommandPath
stat/usr/bin/stat
getent/usr/bin/getent (Linux only)
ps/bin/ps (macOS only)
id/usr/bin/id
sysctl/usr/sbin/sysctl (macOS only)

Using absolute paths prevents infinite recursion when the user has aliased stat to Invoke-BashStat or similar.


The stat -c format specifiers work identically across all platforms:

SpecifierMeaningLinux/macOSWindows
%sSize in bytesexactexact
%aOctal permissionsreal modeapproximated from ACL
%APermission stringreal modeapproximated from ACL
%nFile nameexactexact
%NFull pathexactexact
%UOwner namerealfrom ACL
%GGroup namerealfrom ACL
%iInode numberrealalways 0
%bBlock countrealcalculated from size
%dDevice numberrealderived from drive letter
%YMtime (epoch)exactexact
%hHard link countrealalways 1

All ps-bash commands accept both / and \ as path separators on every platform. When producing BashText output, backslashes are normalized to forward slashes for bash-style display:

Terminal window
$bashLink = $linkName -replace '\\', '/'

This normalization happens in verbose output for cp, mv, ln, mkdir, rmdir, find, and cat error messages.

PowerShell handles ~ expansion natively via its provider system, so ls ~/Documents works on all platforms without ps-bash needing custom logic. The find command resolves the home directory via [System.Environment]::GetFolderPath('UserProfile') when building search roots.


The GitHub Actions workflow (.github/workflows/ci.yml) tests every commit on all three platforms:

strategy:
matrix:
os: [windows-latest, ubuntu-latest, macos-latest]
fail-fast: false

The test suite contains 751 test cases. Platform-conditional skips handle OS-specific limitations:

Test categoryLinuxmacOSWindows
Symlink tests (5 tests)runrunskipped
stat Linux inode testrunskippedskipped
stat Windows device testskippedskippedrun
All other testsrunrunrun

All non-skipped tests pass on every platform. Skipped tests are marked with Pester’s -Skip: parameter tied to platform detection variables ($IsWindows, $IsMacOS, $IsLinux).