placeholderNushell - Cross-Platform

Nushell - Cross-Platform

Encapsulating cross-platform functions and isolating OS-specific configs using Nushell.

In Bash, you can detect the OS using the $OSTYPE variable[1]:

case "$OSTYPE" in  solaris*) echo "SOLARIS" ;;  darwin*)  echo "OSX" ;;  linux*)   echo "LINUX" ;;  bsd*)     echo "BSD" ;;  msys*)    echo "WINDOWS" ;;  # via Git Bash/msysGit  cygwin*)  echo "ALSO WINDOWS" ;; # via Cygwin  *)        echo "unknown: $OSTYPE" ;;esac

Detecting Windows is a bit cumbersome this way. You could use uname instead, but that introduces a new dependency.

Nushell, being a more modern shell, handles this more conveniently. It stores OS-related information — system name, architecture, etc. — in the os-info field of the $nu[2] constant:

# Print $nu.os-info╭────────────────┬─────────╮ name            macos    arch            aarch64  family          unix     kernel_version  25.1.0  ╰────────────────┴─────────╯

Demos

Using this field as a condition, you can easily write cross-platform functions/scripts and keep OS-specific configurations separate.

Cross-Platform Functions/Scripts

Basic commands differ across operating systems (e.g., opening the file manager). You can use match $nu.os-info.name to wrap a unified interface that hides platform differences.

Take opening the file manager: Windows uses explorer.exe, while macOS uses Finder.app. You can write a function that opens the specified folder with the platform’s default file manager:

config.nu
def omni-open [path: string = "."] {  let path = ($path | path expand)  match $nu.os-info.name {    macos => { ^open $path }    windows => { explorer.exe $path }    linux => { ^xdg-open $path } # not tested  }}alias o = omni-open

By the way, Nushell’s built-in start command already does what the function above does, and also supports opening URLs, Obsidian vaults, etc.

Clipboard commands also differ across operating systems — pbcopy on macOS, clip on Windows. You can write a function to copy text cross-platform:

config.nu
def copy_text [] {  match $nu.os-info.name {    "windows" => {      # Windows uses the clip command      $in | ^clip    }    "macos" => {      # macOS uses the pbcopy command      $in | ^pbcopy    }  }}

Usage examples:

# Copy current pathpwd | copy_text# Copy a file's contentsopen README.md | copy_text

Environment variable setup also varies by platform: launchctl setenv on macOS, setx on Windows, and writing to /etc/profile.d/**.sh on Linux. See [[Unix环境变量]] for details. You can write a Nushell function to encapsulate these differences:

def --env env [name: string value: string] {  # Set env var for current session  load-env {$name: $value}  match $nu.os-info.name {    "macos" => {      /bin/launchctl setenv $name $value    }    "linux" => {      let env_file = "/etc/profile.d/gnix-env.sh"      touch $env_file      chmod +x $env_file      let lines = (open $env_file | lines | where not ($it | str starts-with $"export ($name)="))      let env_value = ($value | str replace $nu.home-path "$HOME")      let updated = ($lines | append $"export ($name)=\"($env_value)\"")      $updated | str join (char nl) | save --force $env_file    }    "windows" => {      setx $name $value    }  }}

OS-Specific Configs

Shell config files often contain OS-specific content — Scoop aliases on Windows, Homebrew completion scripts on macOS, etc. PATH settings also vary by system.

You can handle this with conditional logic in config.nu:

Step 1: OS-specific Config

For Windows, create platform/win.nu to hold platform-specific config:

platform/win.nu
$env.YAZI_FILE_ONE = 'D:/envir_vars/scoop/apps/git/current/usr/bin/file.exe'source ../aliases/scoop.nusource ../completions/scoop.nusource ../completions/winget.nudef --env pwd [] { $env.PWD | str replace --all '\' '/'}alias su = scoop updatealias si = scoop installalias sui = scoop uninstallalias sse = scoop searchalias sst = scoop statusalias sl = scoop listalias sbl = scoop bucket listalias sba = scoop bucket addalias sbr = scoop bucket rmalias sui = scoop uninstallalias cdc = cd c://alias cdd = cd d://alias cde = cd e://

Step 2: Conditional Load

In config.nu, write it like this — using Nushell’s source null[3] feature as a no-op:

config.nu
const window_module = if $nu.os-info.name == windows { "./platform/win.nu" } else { null }const mac_module = if $nu.os-info.name == macos { "./platform/mac.nu" } else { null }source $window_modulesource $mac_module

  1. See Bash Variables (Bash Reference Manual) ↩︎

  2. Special Variables | Nushell ↩︎

  3. source | Nushell In Nushell, sourcing null is a no-op. ↩︎

CompactRelaxed
Normal1.70