﻿---
title: Nushell - CrossPlatform
date: 2025-10-16
excerpt: Encapsulating cross-platform functions and isolating OS-specific configs using Nushell
tags:
  - Linux
  - OS
  - macOS
  - Windows
  - Shell
  - Nushell
cover: https://assets.vluv.space/cover/nushell_cross_platform.avif
---

<script data-swup-reload-script type="module" src="/js/components/tab.js"></script>

在Bash中，可以通过 `$OSTYPE` 这个Bash Variable[^1]判断系统信息：

```bash
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
```

其对Windows的判断有点麻烦；可以用 `uname` 程序代替，不过这就引入了新依赖。

Nushell作为相对现代的Shell，这点上做的便利一些，它在 `$nu`[^2] 常量里的 `os-info` 字段中记录了OS相关的信息，包括系统名称，架构 etc.

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

## Demos

使用该字段作为条件，可以方便的编写跨平台函数/脚本，也方便针对不同平台编写对应的配置

### Cross-Platform Functions/Scripts

不同系统的基础命令存在差异（如打开文件管理器），可以通过 `match $nu.os-info.name` 封装统一接口，屏蔽平台差异。

<x-tabs>

<x-tab title="File_Explorer" active>

以打开文件管理器为例，Windows下要使用 `explorer.exe`，而MacOS则为 `Finder.app`；可以编写如下函数实现用平台默认的文件管理器打开指定文件夹

```nu 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
```

BTW，Nushell built-in的 `start` 命令其实实现了上面函数的功能，还支持打开URL，obsidian vault etc.

<video autoplay loop muted playsinline>
   <source src="https://assets.vluv.space/assets-PixPin_2025-10-15_23-53-20.mp4" type="video/mp4" alt="demo">
</video>

</x-tab>

<x-tab title="Clipboard">

不同OS下剪贴板命令也不一样，macOS下使用 `pbcopy` ，Windows下使用 `clip`；可以编写如下函数实现跨平台复制文本到剪贴板

```nu config.nu
def copy_text [] {
  match $nu.os-info.name {
    "windows" => {
      # Windows 使用 clip 命令
      $in | ^clip
    }
    "macos" => {
      # macOS 使用 pbcopy 命令
      $in | ^pbcopy
    }
  }
}
```

使用场景：

```nu
# 复制当前路径
pwd | copy_text
# 复制某个文件的内容
open README.md | copy_text
```

</x-tab>

<x-tab title="envir_vars">

不同平台设置环境变量的方式也不一样，macos下使用 `launchctl setenv`，windows下使用 `setx`，linux的话可以将环境变量写入到 `/etc/profile.d/**.sh` 里，具体可以参考 [[Unix环境变量]]。这里可以编写一个Nushell funciton封装不同OS的差异

```nu
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
    }
  }
}
```

</x-tab>

</x-tabs>

### OS-Specific Configs

Shell里配置文件通常会包含OS-specific的内容，比如Windows下的Scoop别名，macOS下的Homebrew补全脚本等，此外`PATH`的设置也会因系统而异。

对于该问题，可以在 `config.nu` 里通过条件判断实现

#### Step 1: OS-specific Config

以 Windows 为例，创建`platform/win.nu`存放专属配置

 ```nu platform/win.nu
$env.YAZI_FILE_ONE = 'D:/envir_vars/scoop/apps/git/current/usr/bin/file.exe'

source ../aliases/scoop.nu
source ../completions/scoop.nu
source ../completions/winget.nu

def --env pwd [] {
  $env.PWD | str replace --all '\' '/'
}

alias su = scoop update
alias si = scoop install
alias sui = scoop uninstall
alias sse = scoop search
alias sst = scoop status
alias sl = scoop list
alias sbl = scoop bucket list
alias sba = scoop bucket add
alias sbr = scoop bucket rm
alias sui = scoop uninstall
alias cdc = cd c://
alias cdd = cd d://
alias cde = cd e://
```

#### Step 2: Conditional Load

在 `config.nu` 里这样写，这里用到了Nushell里 `source null`[^3] 为no-op(no operation) 的feature

```nu 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_module
source $mac_module
```

[^1]: See [Bash Variables (Bash Reference Manual)](https://www.gnu.org/software/bash/manual/html_node/Bash-Variables.html)
[^2]: [Special Variables | Nushell](https://www.nushell.sh/book/special_variables.html#nu)
[^3]: [source | Nushell](https://www.nushell.sh/commands/docs/source.html) In Nushell, Sourcing `null` is a no-op.
