先确认目标环境: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,或者反过来;这些场景应把可移植性当成硬要求,而不是等失败后再补。
-P 的 PCRE,BSD grep 通常只有 BRE/ERE;Perl 兼容写法要单独确认。-i 直接接表达式,BSD sed 的 -i 接备份后缀,二者不能互换。-d 解析自然语言,BSD 用 -j -f;stat 的格式符是两套体系。-printf、--parents、--max-depth 等是 GNU 扩展,bsdtar 虽有 --strip-components,但其他长选项覆盖不同。readlink -f、sort -V、mktemp -d 无模板等用法,但旧版和精简 BSD 不一定支持,脚本不能只按最新系统假设。| 任务 | 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 |
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。
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。
GNU 的 -c 使用 %s(字节数)、%a(八进制权限)、%Y(mtime epoch);BSD 的 -f 使用 %z、%Lp、%m。macOS 的 stat -x 输出适合人读,但不要用它做脚本解析。只取文件大小时,wc -c < f 两边都可用;复杂元数据建议按平台分支或调用 Python。
详见 stat。
系统自带 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。
GNU xargs 在输入为空时仍会执行一次命令,-r 用来禁止;BSD xargs 默认不执行,macOS 还接受 -r 作为兼容项,所以这个选项在 macOS 上常常“看起来正常”。更可移植的做法是先判空,或用 find -print0 | xargs -0,或直接用 find -exec ... +。
新版 macOS 的 readlink -f 已可用,但旧版 macOS 和部分 BSD 没有这个选项;老脚本常用 realpath、cd "$(dirname "$f")" && pwd -P 或 Perl 的 Cwd::abs_path。注意 pwd -P 只解析目录,不解析最后一级符号链接;要解析整条路径,用 realpath 或语言库更可靠。
详见 realpath。
--strip-components=1 在 GNU tar 和 bsdtar 上都可用,是少数可以直接跨平台的 tar 长选项。差异在扩展能力:GNU 的 --wildcards、--transform 在 bsdtar 上不可用,bsdtar 用 -s 做路径重写;反过来,bsdtar 对某些归档格式和扩展属性的处理也不同。先用 tar --version 确认实现。
详见 tar。
ps aux 在两边都能跑,但 GNU 的 --sort、--forest 和 BSD 的 -m、-r 不通用;脚本应显式选择字段并用 sort 处理。du 的 -d 1 在 GNU 和现代 BSD 上都成立,--max-depth 只有 GNU 支持,-b 也不是 BSD 的通用选项。
旧 BSD 的 mktemp 通常要求模板参数,且模板末尾至少 6 个 X;macOS 新版本允许 mktemp -d 无模板,但脚本不应依赖这一行为。可移植写法是显式使用 TMPDIR 并给出模板,同时加引号避免空格问题。
dir=$(mktemp -d "${TMPDIR:-/tmp}/myapp.XXXXXX")
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。
Linux 的 hostname -I 和 nproc 在 macOS 上不存在;macOS 用 ipconfig getifaddr en0 取指定接口地址,用 sysctl -n hw.ncpu 取逻辑 CPU 数。容器里 nproc 可能读到 cpuset 限制后的值,做并发度计算时要结合实际配额。
详见 hostname、macOS ifconfig、nproc 与 macOS sysctl。
macOS 用 pbcopy/pbpaste,Linux 图形环境用 xclip 或 wl-copy;无 DISPLAY 或纯 SSH 会话时都不可用。不要把剪贴板命令写进无人值守脚本的关键路径。
详见 macOS pbcopy 与 xclip。
uname 分支。command -v、--version 探测或小样本试跑。gsed、gdate、gstat,不要覆盖系统命令。ls、ps 的默认列宽;用 find -print0、显式 ps -o 字段和结构化工具。#!/usr/bin/env bash 时注意 macOS 自带 bash 仍是 3.2,不要使用关联数组、mapfile 等新特性;需要新 bash 时通过 Homebrew 安装并在 shebang 或 CI 中固定路径。-- 结束选项,BSD 与 GNU 对以连字符开头的文件名都容易误判。jq,少写 sed 魔法。sed、date、stat 和 Linux 参数一致。grep -P 在所有 grep 上都可用;macOS 自带 grep 通常没有。xargs -r 在所有 BSD 上生效;macOS 只是兼容接受,老 BSD 可能直接报错。readlink -f 在所有 macOS 上都有;新版有,旧版没有。--wildcards、--transform 带到 bsdtar。hostname -I 在 macOS 取 IP,或用 nproc 在 macOS 取核数。g* 工具默认存在;干净 CI 镜像里通常没有。合上页面,先回答,再展开对照。
sed -i 's/a/b/' f 在 macOS 上为什么失败?两种可用的写法是什么?date -d?两套 date 的 -r 语义有什么不同?readlink -f、xargs -r 和 mktemp -d?-i 的下一个独立参数当成备份后缀,'s/a/b/' 被误认为后缀,所以报错。可用 sed -i '' -e 's/a/b/' f(空后缀表示不备份),或写成 sed 's/a/b/' f > f.tmp && mv f.tmp f,后者最可移植。date -j -f '%Y-%m-%d' '2026-01-01' +%s 解析日期,用 -v+1d 做日期运算。-r 在 BSD 上表示 epoch 秒,在 GNU 上表示参考文件的修改时间;两者不能混用,跨平台脚本应按 uname 分支或使用 Python/Perl。readlink -f 在旧 macOS 上可能不存在,改用 realpath、cd ... && pwd -P 或语言库;xargs -r 在 macOS 上是兼容项,老 BSD 可能不支持,最稳的是先判空或用 find -exec;mktemp -d 在旧 BSD 上通常要求模板,写成 mktemp -d "${TMPDIR:-/tmp}/x.XXXXXX"。
小程序版
微信扫码,随时查命令