igozhang

——

    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,如 1005001024):

    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 秒。

    MP3