系统学习 学习路径总入口:先选领域,再建立模型,最后用故障演练验证

GNU 与 BSD/macOS 命令参数差异:18 组对照、可移植写法与踩坑

使用方式

先确认目标环境:Linux 通常使用 GNU coreutils、findutils 和 GNU tar;macOS 使用 BSD 用户态和 bsdtar,但新版 macOS 补上了部分 GNU 选项,Homebrew 也可以安装带 g 前缀的 GNU 工具。写一次性命令时可以直接用本机写法;写脚本、Dockerfile、CI 步骤或跨平台文档时,优先选择两边都成立的写法,或者显式检测命令能力。

本页的“Linux 写法”以 GNU 工具为准,“macOS 写法”以系统自带 BSD 工具为准;安装 coreutils 后的 gsed、gdate、gstat、gxargs、greadlink、gsort 等不在默认环境中,不能直接写进通用脚本。macOS 分支只收录了部分命令页,未收录的以 linux-vs-macos 的对照为准。

如果只是日常在单机执行,记不住全部差异也没关系:先按本机 man page 写,再把要复用的命令放进脚本时逐条核对。真正容易出问题的是 CI、容器构建和发布脚本,因为它们经常从 Linux 跑到 macOS runner,或者反过来;这些场景应把可移植性当成硬要求,而不是等失败后再补。

总体差异

命令对照表

任务 Linux/GNU macOS/BSD 可移植写法或注意 页面
原地替换 sed -i 's/a/b/g' f sed -i '' -e 's/a/b/g' f BSD 的 -i 后必须接备份后缀,空串表示不备份 sed
日期解析 date -d '2026-01-01' +%s date -j -f '%Y-%m-%d' '2026-01-01' +%s BSD -r 是 epoch 秒,GNU -r 是参考文件,语义不同 date
文件元数据 stat -c '%s %a %Y' f stat -f '%z %Lp %m' f 格式符不通用;macOS 可用 stat -x 看可读输出 stat
PCRE 匹配 grep -P '\d+' f 无 -P,用 grep -E '[0-9]+' 或 ggrep -P \d 不是可移植 ERE,写成 [0-9] 更稳 grep
空输入处理 xargs -r echo < /dev/null xargs echo < /dev/null GNU 无 -r 会执行一次;macOS 接受 -r 但只是兼容项 xargs
真实路径 readlink -f link 新版可 readlink -f;老版用 realpath 或 cd ... && pwd -P readlink -f 不是所有旧 BSD 都有,不要只测最新 macOS realpath
解包去顶层 tar xzf a.tgz --strip-components=1 bsdtar 同样支持 --strip-components=1 bsdtar 不支持 GNU 的 --wildcards、--transform tar
进程排序 ps -eo pid,ppid,cmd --sort=-%mem ps -Ao pid,ppid,comm -m ps aux 两边可用,但列宽、命令名截断和排序选项不同 ps · macOS ps
磁盘占用 du -sh --max-depth=1 . du -h -d 1 . -d 1 两边都支持;--max-depth 和 -b 是 GNU 扩展 du
临时目录 mktemp -d mktemp -d "${TMPDIR:-/tmp}/x.XXXXXX" 旧 BSD 通常要求模板,模板至少 6 个 X 结尾 mktemp
版本排序 sort -V 新版支持 sort -V;老版用 gsort -V 老 BSD 没有 -V,纯数字才可退回 sort -n sort
Base64 不换行 base64 -w0 f base64 -b 0 f,或输出后删除换行 解码 GNU 用 -d,BSD 传统用 -D base64
保留目录结构复制 cp --parents a/b/c dest 无 --parents,用 rsync -aR a/b/c dest/ rsync -R 比手写 mkdir -p 加 cp 更稳 cp · macOS cp
彩色列表 ls --color=auto ls -G 新版 macOS 也接受 --color,旧版不一定;脚本不要解析颜色 ls · macOS ls
按格式列出文件 find . -name '*.log' -printf '%s %p\n' find . -name '*.log' -exec stat -f '%z %N' {} + -print0 两边可用,-printf 是 GNU 扩展 find · macOS find
取主机 IP hostname -I ipconfig getifaddr en0 macOS 接口名因 Wi-Fi、有线和虚拟网卡而异,先 ifconfig 确认 hostname · macOS ifconfig
CPU 数量 nproc sysctl -n hw.ncpu 容器里 nproc 可能受 cpuset 限制,不等于物理核数 nproc · macOS sysctl
剪贴板 xclip -selection clipboard pbcopy / pbpaste 无图形环境或纯 SSH 时都不可用,需要额外转发 macOS pbcopy · xclip

逐组说明

sed:备份后缀的位置

GNU sed 把 -i 后面的内容当作可选后缀,所以 sed -i.bak 's/a/b/' f 会留下 f.bak;BSD sed 把 -i 的下一个独立参数当作备份后缀,sed -i '' -e 's/a/b/' f 才是不备份的写法。最可移植的方式是输出到临时文件再替换,避免两边语法差异。

sed 's/a/b/g' f > f.tmp && mv f.tmp f

详见 sed 与 sed-cheatsheet。

date:解析、epoch 与 -r

GNU date 的 -d 能解析 2026-01-01、+1 day 等字符串;BSD date 用 -j -f 格式 输入 解析,用 -v+1d 做日期运算。-r 的差异最容易被忽略:BSD 的 date -r 0 把参数当 epoch 秒,GNU 的 date -r file 读取文件修改时间。

date -d '2026-01-01 00:00:00' +%s
date -j -f '%Y-%m-%d %H:%M:%S' '2026-01-01 00:00:00' +%s
date -r 1767225600

跨平台脚本可用 Python 或 Perl 做时间计算,或者按 uname 分支。详见 date。

stat:格式符是两套体系

GNU 的 -c 使用 %s(字节数)、%a(八进制权限)、%Y(mtime epoch);BSD 的 -f 使用 %z、%Lp、%m。macOS 的 stat -x 输出适合人读,但不要用它做脚本解析。只取文件大小时,wc -c < f 两边都可用;复杂元数据建议按平台分支或调用 Python。

详见 stat。

grep:-P 与正则方言

系统自带 macOS grep 是 BSD grep,不支持 -P;需要 PCRE 时安装 GNU grep 并调用 ggrep -P,或改用 rg。grep -E 两边都有,但 \d、\s 不是 POSIX ERE 的一部分,写成 [0-9]、[[:space:]] 更稳。--include 等 GNU 选项也不应默认存在,跨平台可用 find ... -exec grep。

详见 grep 与 grep-cheatsheet。

xargs:空输入与 -r

GNU xargs 在输入为空时仍会执行一次命令,-r 用来禁止;BSD xargs 默认不执行,macOS 还接受 -r 作为兼容项,所以这个选项在 macOS 上常常“看起来正常”。更可移植的做法是先判空,或用 find -print0 | xargs -0,或直接用 find -exec ... +。

详见 xargs 与 find。

新版 macOS 的 readlink -f 已可用,但旧版 macOS 和部分 BSD 没有这个选项;老脚本常用 realpath、cd "$(dirname "$f")" && pwd -P 或 Perl 的 Cwd::abs_path。注意 pwd -P 只解析目录,不解析最后一级符号链接;要解析整条路径,用 realpath 或语言库更可靠。

详见 realpath。

tar:--strip-components 与 bsdtar

--strip-components=1 在 GNU tar 和 bsdtar 上都可用,是少数可以直接跨平台的 tar 长选项。差异在扩展能力:GNU 的 --wildcards、--transform 在 bsdtar 上不可用,bsdtar 用 -s 做路径重写;反过来,bsdtar 对某些归档格式和扩展属性的处理也不同。先用 tar --version 确认实现。

详见 tar。

ps 与 du:排序和深度

ps aux 在两边都能跑,但 GNU 的 --sort、--forest 和 BSD 的 -m、-r 不通用;脚本应显式选择字段并用 sort 处理。du 的 -d 1 在 GNU 和现代 BSD 上都成立,--max-depth 只有 GNU 支持,-b 也不是 BSD 的通用选项。

详见 ps、macOS ps 与 du。

mktemp:模板与 TMPDIR

旧 BSD 的 mktemp 通常要求模板参数,且模板末尾至少 6 个 X;macOS 新版本允许 mktemp -d 无模板,但脚本不应依赖这一行为。可移植写法是显式使用 TMPDIR 并给出模板,同时加引号避免空格问题。

dir=$(mktemp -d "${TMPDIR:-/tmp}/myapp.XXXXXX")

详见 mktemp 与 tempfile。

GNU 扩展:find -printf、cp --parents、ls 颜色

find -printf 只有 GNU find 支持,BSD 用 -exec stat;cp --parents 只有 GNU cp 支持,BSD 用 rsync -aR;彩色 ls 在旧版 macOS 用 -G,新版也接受 --color。脚本不应解析 ls 输出,也不要把颜色选项当成稳定接口。

详见 find、macOS find、cp 与 macOS cp。

主机与 CPU:hostname -I、nproc

Linux 的 hostname -I 和 nproc 在 macOS 上不存在;macOS 用 ipconfig getifaddr en0 取指定接口地址,用 sysctl -n hw.ncpu 取逻辑 CPU 数。容器里 nproc 可能读到 cpuset 限制后的值,做并发度计算时要结合实际配额。

详见 hostname、macOS ifconfig、nproc 与 macOS sysctl。

剪贴板:pbcopy 与 xclip

macOS 用 pbcopy/pbpaste,Linux 图形环境用 xclip 或 wl-copy;无 DISPLAY 或纯 SSH 会话时都不可用。不要把剪贴板命令写进无人值守脚本的关键路径。

详见 macOS pbcopy 与 xclip。

可移植脚本建议

常见误区

参考链接

读完自测

合上页面,先回答,再展开对照。

  1. sed -i 's/a/b/' f 在 macOS 上为什么失败?两种可用的写法是什么?
  2. BSD 上如何替代 GNU date -d?两套 date 的 -r 语义有什么不同?
  3. 写跨 Linux 和 macOS 的脚本时,应该如何处理 readlink -f、xargs -r 和 mktemp -d?
答案
  1. BSD sed 把 -i 的下一个独立参数当成备份后缀,'s/a/b/' 被误认为后缀,所以报错。可用 sed -i '' -e 's/a/b/' f(空后缀表示不备份),或写成 sed 's/a/b/' f > f.tmp && mv f.tmp f,后者最可移植。
  2. BSD 用 date -j -f '%Y-%m-%d' '2026-01-01' +%s 解析日期,用 -v+1d 做日期运算。-r 在 BSD 上表示 epoch 秒,在 GNU 上表示参考文件的修改时间;两者不能混用,跨平台脚本应按 uname 分支或使用 Python/Perl。
  3. readlink -f 在旧 macOS 上可能不存在,改用 realpath、cd ... && pwd -P 或语言库;xargs -r 在 macOS 上是兼容项,老 BSD 可能不支持,最稳的是先判空或用 find -exec;mktemp -d 在旧 BSD 上通常要求模板,写成 mktemp -d "${TMPDIR:-/tmp}/x.XXXXXX"。

相关条目

微信小程序码:扫码打开命令速查小程序

小程序版

微信扫码,随时查命令