Mac 常用命令
适用于 Apple Silicon Mac(M1 及以后)。后续持续补充。
系统概况 macinfo
一条命令汇总 macOS 版本、硬件、CPU/GPU、内存、磁盘、电池与运行时长。
命令
echo "========== Mac 系统概况 =========="; \
echo "[系统版本]"; sw_vers; \
echo; echo "[架构]"; echo " $(uname -m) (Apple Silicon)"; \
echo; echo "[硬件信息]"; \
system_profiler SPHardwareDataType 2>/dev/null | grep -E "^\s+(Model Name|Model Identifier|Chip|Total Number of Cores|Memory|Serial Number|Hardware UUID)" | sed 's/^[[:space:]]*//'; \
echo; echo "[CPU 核心]"; echo " 物理/逻辑: $(sysctl -n hw.physicalcpu) / $(sysctl -n hw.logicalcpu)"; \
echo; echo "[GPU 信息]"; \
system_profiler SPDisplaysDataType 2>/dev/null | grep -E "^\s+(Chipset Model|Total Number of Cores|Metal Support)" | sed 's/^[[:space:]]*//'; \
echo; echo "[内存]"; echo " $(( $(sysctl -n hw.memsize) / 1073741824 )) GB"; \
echo; echo "[磁盘 (整盘)]"; \
diskutil apfs list 2>/dev/null | awk '/Capacity Ceiling/{if(match($0,/\([0-9.]+ GB\)/)) total=substr($0,RSTART+1,RLENGTH-2)} /Capacity In Use By Volumes/{if(match($0,/\([0-9.]+ GB\)/)) used=substr($0,RSTART+1,RLENGTH-2); if(match($0,/([0-9.]+%) used/)) pct=substr($0,RSTART,RLENGTH-5)} /Capacity Not Allocated/{if(match($0,/\([0-9.]+ GB\)/)) free=substr($0,RSTART+1,RLENGTH-2); printf " 总容量 %s | 已用 %s (%s) | 可用 %s\n", total, used, pct, free}' | head -1; \
echo; echo "[用户数据卷]"; df -h /System/Volumes/Data | awk 'NR==1{next} {printf " 已用 %s | 可用 %s\n", $3, $4}'; \
echo; echo "[电池]"; pmset -g batt 2>/dev/null | tail -1 | sed 's/^\t//'; \
echo; echo "[运行时长]"; uptime | sed 's/.* up / up /'; \
echo "=================================="
设为别名(可选)
alias macinfo='echo "========== Mac 系统概况 =========="; echo "[系统版本]"; sw_vers; echo; echo "[架构]"; echo " $(uname -m) (Apple Silicon)"; echo; echo "[硬件信息]"; system_profiler SPHardwareDataType 2>/dev/null | grep -E "^\s+(Model Name|Model Identifier|Chip|Total Number of Cores|Memory|Serial Number|Hardware UUID)" | sed "s/^[[:space:]]*//"; echo; echo "[CPU 核心]"; echo " 物理/逻辑: $(sysctl -n hw.physicalcpu) / $(sysctl -n hw.logicalcpu)"; echo; echo "[GPU 信息]"; system_profiler SPDisplaysDataType 2>/dev/null | grep -E "^\s+(Chipset Model|Total Number of Cores|Metal Support)" | sed "s/^[[:space:]]*//"; echo; echo "[内存]"; echo " $(( $(sysctl -n hw.memsize) / 1073741824 )) GB"; echo; echo "[磁盘 (整盘)]"; diskutil apfs list 2>/dev/null | awk "/Capacity Ceiling/{if(match(\$0,/\\([0-9.]+ GB\\)/)) total=substr(\$0,RSTART+1,RLENGTH-2)} /Capacity In Use By Volumes/{if(match(\$0,/\\([0-9.]+ GB\\)/)) used=substr(\$0,RSTART+1,RLENGTH-2); if(match(\$0,/([0-9.]+%) used/)) pct=substr(\$0,RSTART,RLENGTH-5)} /Capacity Not Allocated/{if(match(\$0,/\\([0-9.]+ GB\\)/)) free=substr(\$0,RSTART+1,RLENGTH-2); printf \" 总容量 %s | 已用 %s (%s) | 可用 %s\\n\", total, used, pct, free}" | head -1; echo; echo "[用户数据卷]"; df -h /System/Volumes/Data | awk "NR==1{next} {printf \" 已用 %s | 可用 %s\\n\", \$3, \$4}"; echo; echo "[电池]"; pmset -g batt 2>/dev/null | tail -1 | sed "s/^\t//"; echo; echo "[运行时长]"; uptime | sed "s/.* up / up /"; echo "=================================="'
大文件扫描 bigfiles
10 秒内列出实际磁盘占用最大的 25 个文件(默认 >100MB)。核心用 Spotlight 索引(mdfind),比全盘 find 快一个数量级;并对 Downloads / Desktop / 废纸篓 / Applications 做 find 补漏(覆盖尚未被索引的新文件)。
只需改第一行 MIN_MB 即可(单位 MB,如 100、500、1024):
MIN_MB=100; MIN="${MIN_MB}M"; MIN_BYTES=$((MIN_MB * 1048576)); \
echo "========== 大文件扫描 (>${MIN}, 按实际占用) =========="; \
echo "[扫描范围] $HOME、/Applications + 关键目录补漏"; \
echo "[Top 25 文件]"; \
{ mdfind -onlyin "$HOME" "kMDItemFSSize > ${MIN_BYTES}" 2>/dev/null; \
mdfind -onlyin /Applications "kMDItemFSSize > ${MIN_BYTES}" 2>/dev/null; \
find "$HOME/Downloads" "$HOME/Desktop" "$HOME/.Trash" /Applications -type f -size +"${MIN}" 2>/dev/null; \
} | sort -u | head -400 | while IFS= read -r f; do [ -f "$f" ] && stat -f '%b %z %N' "$f"; done 2>/dev/null \
| awk -v min="$MIN_BYTES" 'BEGIN{split("B KB MB GB TB",u," ")}
function hum(b, h,i){h=b;i=1;while(h>=1024&&i<5){h/=1024;i++} return sprintf("%7.1f %-2s",h,u[i])}
{blocks=$1;logical=$2;$1=$2="";path=substr($0,2);phys=blocks*512;
if(phys<min) next;
extra=(logical>phys*11/10)?sprintf(" (逻辑 %s)",hum(logical)):"";
printf "%020d\t%s%s\t%s\n",phys,hum(phys),extra,path}' \
| sort -rn | head -25 | cut -f2- | awk -F'\t' '{printf " %s %s\n",$1,$2}'; \
echo "========================================="
进程占用 Top N topprocs
快照式列出 CPU / 内存占用最高的进程(默认 Top 5),含系统整体 CPU 与内存概览。实测 <1 秒。
只需改第一行 TOP_N 即可:
TOP_N=5; \
echo "========== 进程占用 Top ${TOP_N} =========="; \
echo "[系统概览]"; top -l 1 -s 0 -n 0 2>/dev/null | grep -E "CPU usage|PhysMem" | sed 's/^/ /'; \
echo; echo "[CPU Top ${TOP_N}]"; \
ps -A -o pid=,pcpu=,pmem=,rss=,comm= | sort -nrk 2 | head -"${TOP_N}" | awk 'function bn(s,i){i=match(s,/[^\/]+$/);return substr(s,i)}
{pid=$1;cpu=$2;mem=$3;rss=$4;$1=$2=$3=$4="";sub(/^ +/,"");name=bn($0);if(length(name)>36)name=substr(name,1,33)"...";printf " %5.1f%% CPU %5.1f%% MEM %6.0f MB [%s] %s\n",cpu,mem,rss/1024,pid,name}' || true; \
echo; echo "[内存 Top ${TOP_N}]"; \
ps -A -o pid=,pcpu=,pmem=,rss=,comm= | sort -nrk 3 | head -"${TOP_N}" | awk 'function bn(s,i){i=match(s,/[^\/]+$/);return substr(s,i)}
{pid=$1;cpu=$2;mem=$3;rss=$4;$1=$2=$3=$4="";sub(/^ +/,"");name=bn($0);if(length(name)>36)name=substr(name,1,33)"...";printf " %5.1f%% CPU %5.1f%% MEM %6.0f MB [%s] %s\n",cpu,mem,rss/1024,pid,name}' || true; \
echo "========================================="
说明
- CPU %: 多核机器上单进程可超过 100%(如 8 核满载约 800%),属正常现象。
- MEM % / MB:
%MEM占总内存比例,MB为 RSS 常驻物理内存。 - 快照: 瞬时采样,非活动监视器那样的持续平均值;多次运行结果会有波动。
网络信息 netinfo
汇总默认网关、网卡/IP、硬件端口、Wi-Fi、DNS、VPN/隧道、路由表等。纯系统命令,<1 秒。
echo "========== 网络信息 =========="; \
echo "[默认路由]"; \
route -n get default 2>/dev/null | awk -F: '/gateway:|interface:/{gsub(/^ +/,"",$2); if($1~/gateway/) g=$2; if($1~/interface/) i=$2} END{printf " 网关 %s 接口 %s\n", g, i}'; \
echo; echo "[主接口]"; \
scutil --nwi 2>/dev/null | awk '/^[ \t]+[a-z0-9]+ :/{iface=$1; sub(/:$/,"",iface)} /^[ \t]+address/{printf " %-6s %s\n", iface, $3}'; \
echo; echo "[网卡与 IP]"; \
ifconfig 2>/dev/null | awk '/^[^ \t]/ {sub(/:$/,"",$1); iface=$1} /^\tinet / && $2!="127.0.0.1" {printf " %-8s IPv4 %s\n", iface, $2} /^\tinet6 / && $2!~/^fe80:/ && $2!~/^::1/ {printf " %-8s IPv6 %s\n", iface, $2}'; \
echo; echo "[硬件端口]"; \
networksetup -listallhardwareports 2>/dev/null | awk '/Hardware Port:/{port=$0; sub(/.*: /,"",port)} /^Device:/{dev=$2} /^Ethernet Address:/{printf " %-24s %-6s %s\n", port, dev, $3}'; \
echo; echo "[Wi-Fi]"; \
networksetup -getairportnetwork en0 2>/dev/null | sed 's|Current Wi-Fi Network: | SSID: |; /^You are not/s/^/ /'; \
networksetup -getinfo "Wi-Fi" 2>/dev/null | grep -E "^(IP address|Subnet mask|Router|Wi-Fi ID):" 2>/dev/null | sed 's/^/ /' || true; \
echo; echo "[DNS (主 resolver)]"; \
scutil --dns 2>/dev/null | awk '/^resolver #1$/{on=1; next} on&&/^$/{exit} on&&/nameserver\[/{print " "$0} on&&/search domain\[/{print " "$0} on&&/if_index :/{print " "$0}'; \
echo; echo "[VPN / 隧道]"; \
scutil --nc list 2>/dev/null | sed 's/^[ \t]*/ /'; \
ifconfig 2>/dev/null | awk '/^utun/{sub(/:$/,"",$1); u=$1} /^\tinet / && u {printf " %s IPv4 %s (隧道)\n", u, $2; u=""}'; \
echo; echo "[路由表 (IPv4 摘要)]"; \
netstat -rn -f inet 2>/dev/null | awk 'NR<=3||/^default/||/utun/' | head -12 | sed 's/^/ /' || true; \
echo "=================================="
说明
- 主接口:
scutil --nwi显示当前用于上网的接口与地址。 - utun: 有 IP 的
utun通常表示 VPN/系统隧道(即使 VPN 显示 Disconnected 也可能存在残留隧道)。 - 路由表: 仅展示默认路由与
utun相关条目;完整路由用netstat -rn。
网络 & 磁盘 IO Top N netio
统计系统网络/磁盘 IO 概况,并列出 Top N 进程(默认 5)。网络用 nettop(无需 sudo),磁盘进程用 fs_usage(需 sudo)。改 TOP_N / IO_SEC 即可。
TOP_N=5; IO_SEC=2; \
echo "========== 网络 & 磁盘 IO Top ${TOP_N} =========="; \
echo "[系统概览]"; \
top -l 1 -s 0 -n 0 2>/dev/null | grep -E "^(Networks|Disks):" | sed 's/^/ /'; \
iostat -d -c 1 disk0 2>/dev/null | awk 'NR==3{printf " 磁盘 disk0: %s tps, %s MB/s (瞬时速率)\n",$2,$3}'; \
echo; echo "[网络 IO Top ${TOP_N} 进程 (${IO_SEC}s 采样)]"; \
nettop -P -L 2 -s "${IO_SEC}" -J bytes_in,bytes_out -x 2>/dev/null | awk -v sec="${IO_SEC}" '
function pname(k){sub(/\.[0-9]+$/,"",k);return k}
/^,bytes_in/{sample++;next}
sample>=1&&index($0,",")>0{
split($0,f,","); if(length(f)<3) next;
key=f[1]; bin=f[2]+0; bout=f[3]+0;
if(sample==1){in1[key]=bin;out1[key]=bout}
else if(sample==2&&(key in in1)){
p=pname(key); din[key]=(bin-in1[key])/sec; uo[key]=(bout-out1[key])/sec
}
}
END{
for(key in din){p=pname(key); di[p]+=din[key]; uo2[p]+=uo[key]}
for(p in di){tot=di[p]+uo2[p]; if(tot>0) print tot,di[p],uo2[p],p}
}' | sort -rnk1 | head -"${TOP_N}" | awk '
function hum(b,h,i,u){split("B KB MB GB TB",u," ");h=b;i=1;while(h>=1024&&i<5){h/=1024;i++}return sprintf("%.1f %s",h,u[i])}
{printf " 总 %6s/s (↓%6s/s ↑%6s/s) %s\n", hum($1), hum($2), hum($3), $4}' || true; \
echo; echo "[磁盘 IO Top ${TOP_N} 进程 (${IO_SEC}s 采样, 需 sudo)]"; \
if sudo -n true 2>/dev/null || sudo -v 2>/dev/null; then \
_disk=$(sudo fs_usage -f filesystem -w -e -t "${IO_SEC}" 2>/dev/null | awk -v sec="${IO_SEC}" -v maxb=67108864 '
function hex2dec(v,h,i,c,d,n){h=substr(v,3);n=0;for(i=1;i<=length(h);i++){c=tolower(substr(h,i,1));if(c>="0"&&c<="9")d=index("0123456789",c)-1;else d=index("0123456789abcdef",c)+9;n=n*16+d}return n}
function bparse(x,a,v,b){split(x,a,"=");v=a[2];if(v~/^0x/)b=hex2dec(v);else b=v+0;if(b<=0||b>maxb)return 0;return b}
function isdataio(c){return c~/read|write|PgIn|PgOut|RdData|WrData|pread|pwrite/i && c!~/getattr|attrlist|open|close|stat|mmap|truncate|clone|copyfile|sendfile/i}
function pname(x){sub(/\.[0-9]+$/,"",x);return x}
function revstr(s,r,i){r="";for(i=length(s);i>=1;i--)r=r substr(s,i,1);return r}
function validcand(c){return c!~/0x/ && c!~/^x[0-9a-fA-F]/ && c!~/^[0-9]/ && length(c)>=4}
function getproc(line, pos,s,cand,p,m, rev,rp,out,j,c){
p=""; s=line; pos=1;
while(pos<=length(s) && match(substr(s,pos),/([A-Za-z_][A-Za-z0-9_ .()-]{1,60}\.[0-9]+)[ \t]+\//)){
m=substr(s,pos); cand=substr(m,RSTART,RLENGTH); sub(/[ \t]+\/.*/,"",cand);
if(validcand(cand)) p=cand;
pos+=RSTART+(validcand(cand)?RLENGTH:1);
}
if(p!="") return p;
if(match(line,/\/[^ \t]+[ \t]+([A-Za-z_][A-Za-z0-9_ .()-]{1,60}\.[0-9]+)[ \t]*$/)){
p=substr(line,RSTART,RLENGTH); sub(/^[^ \t]+[ \t]+/,"",p); sub(/[ \t]+$/,"",p);
if(validcand(p)) return p;
}
rev=revstr(line);
if(!match(rev,/^[0-9]+\./)) return "";
rp=substr(rev,RSTART+RLENGTH); out="";
for(j=1;j<=length(rp)&&j<=100;j++){
c=substr(rp,j,1);
if(c==" " && substr(rp,j+1,1)==" ") break;
if(c=="x" && substr(rp,j+1,1)~/[0-9a-fA-F]/ && j<=3) break;
out=out c;
}
p=revstr(out); sub(/^[ \t]+/,"",p); sub(/[ \t]+$/,"",p);
if(p~/^[A-Za-z_]/ && validcand(p)) return p;
return "";
}
{
line=$0; call=$2; if(!isdataio(call)) next;
if(!match(line,/ [ \t]B=(0x[0-9A-Fa-f]+|[0-9]+)/)) next;
btag=substr(line,RSTART,RLENGTH); sub(/^[ \t]*B=/,"",btag); bytes=bparse("B=" btag);
proc=getproc(line); if(proc==""||bytes==0) next; n=pname(proc); if(n=="fs_usage") next;
t[n]+=bytes;
if(call~/Wr|write|Pgout|scatter|DKIO/i) w[n]+=bytes; else if(call~/Rd|read|PgIn|gather|pread/i) r[n]+=bytes;
}
END{for(n in t) printf "%s %s %s %s\n", t[n]/sec, r[n]/sec, w[n]/sec, n}'); \
if [ -n "$_disk" ]; then echo "$_disk" | sort -rnk1 | head -"${TOP_N}" | awk '
function hum(b,h,i,u){split("B KB MB GB TB",u," ");h=b;i=1;while(h>=1024&&i<5){h/=1024;i++}return sprintf("%.1f %s",h,u[i])}
{printf " 总 %6s/s (读 %6s/s 写 %6s/s) %s\n", hum($1), hum($2), hum($3), $4}'; \
else echo " (采样期间无明显磁盘 IO)"; fi; \
else echo " 跳过(需要 sudo,macOS 进程级磁盘 IO 需 fs_usage)"; fi; \
echo "========================================="
说明
- 网络进程:
nettop两次采样算速率,↓接收 ↑发送,同名 Helper 进程会合并。 - 磁盘系统:
top/iostat为整盘概况;iostat为瞬时吞吐。 - 磁盘进程: 只统计 read/write 类 syscall;排除
mmap/truncate等(其B=表文件大小,非实际传输,会产生 TB 级误报);单次 syscall 上限 64MB。 - 耗时: 约
IO_SEC × 2秒(网络)+IO_SEC秒(磁盘,若 sudo 可用),默认 ~4–6 秒。