当前位置:首页>Linux>每天学一个Linux命令系列(39):time - 你的脚本跑多久?time命令测性能

每天学一个Linux命令系列(39):time - 你的脚本跑多久?time命令测性能

  • 2026-10-11 07:02:15
每天学一个Linux命令系列(39):time - 你的脚本跑多久?time命令测性能
运
运维少年 · 科技观察

这个命令是干啥的

优化脚本之前,你得先知道它到底慢在哪。time 就是干这个的 - 算一个命令从开始到结束花了多少时间。

有次我写了个数据备份脚本,每天早上定时跑,但越来越慢。最开始几秒就跑完了,后来要跑半小时。我大概知道是数据量大了所以慢,但具体慢在哪个环节不好说。

这时候 time 就派上用场了。给脚本前面加个 time 一跑,时间数据清清楚楚,再配合分段计时,马上找到瓶颈在哪。

time 可以是个shell内建命令(bash自带的),也可以是个独立的系统工具(/usr/bin/time)。两个输出的信息量差别挺大。

基本用法(3分钟上手)

最简单的用法

# 统计 sleep 命令的执行时间
time sleep 2

输出:

real    0m2.003s
user    0m0.000s
sys     0m0.002s

给任何命令前面加 time

# 看看ping一个网站花多久
time ping -c 4 baidu.com

# 统计脚本执行时间
time ./backup.sh

# 统计命令管道的时间
time tar -czf backup.tar.gz /data | ssh server "cat > backup.tar.gz"

进阶骚操作

读懂 real、user、sys 三个时间

这是 time 最核心的概念,搞懂了才算会用。

# 用计算密集的任务来演示
time find / -name "*.log" 2>/dev/null

比如上面 find 命令跑完,输出可能是:

real    0m12.345s
user    0m1.200s
sys     0m3.100s

real(实际时间): 从按下回车到命令结束,墙上的钟走了多久。包括等CPU、等IO、被别的进程挤占的时间。也就是你"感觉"到的时间。

user(用户态时间): 程序自己在用户空间跑代码花的时间。相当于真正干活的CPU时间。

sys(内核态时间): 程序调系统调用(读写文件、网络通信)花的时间。这部分时间花在内核里。

三者关系:
- user + sys 约等于程序消耗的CPU总时间
- real 往往大于 user + sys,因为程序需要等(等磁盘、等网络、等调度)
- 多核机器上 real 可能小于 user + sys,因为多个CPU同时干活

举个例子:你调了个菜,厨师花了5分钟做(user),中间让帮手切菜花了2分钟(sys),但因为前面排了3桌(其他进程),最后等了15分钟才吃上(real)。

多次运行取平均

单次运行的时间可能有波动(磁盘缓存、CPU调度都有影响)。正确做法是跑多次取平均:

# 用循环跑10次,统计每次的时间
for i in {1..10}; do
    time ./my_script.sh 2>&1
done

# 或者用专用工具 hyperfine(需要安装)
# hyperfine './my_script.sh'

格式化输出

shell内建的 time 格式比较固定。想自定义输出格式,得用外部的 /usr/bin/time:

# 使用外部time命令
/usr/bin/time -f "用时: %E, CPU: %P, 内存: %M KB" ls

# 自定义格式输出
/usr/bin/time -f "\n真实时间: %E\n用户态: %U\n核心态: %S\nCPU占比: %P\n最大内存: %M KB\n上下文切换: %c+%w" ./script.sh

常用格式变量:
- %E - real时间(小时:分:秒)
- %U - user时间(秒)
- %S - sys时间(秒)
- %P - CPU使用百分比
- %M - 最大常驻内存(KB)
- %K - 平均内存总量(KB)

输出重定向的问题

注意 time 的输出是到 stderr(标准错误),不是 stdout。想保存到文件:

# 正确做法:把 stderr 重定向到文件
time ./script.sh 2> time.log

# 如果想分开保存:标准输出不要,时间保留
time ./script.sh > /dev/null 2> time.log

内建 time vs /usr/bin/time

# 查看当前用的是内建还是外部
type time

# bash内建 version:输出为 real/m sys/user 格式
# 外部 version:支持更多格式选项

区别:
- bash内建:简单快速,但输出格式固定,不支持 -f
- /usr/bin/time:功能丰富,支持格式控制、内存统计

# 强制用外部命令
/usr/bin/time -v ./script.sh
# -v 参数输出非常详细的信息

避坑指南

坑1:time 的统计管道不准

# 这样统计会把整个管道的所有进程一起算
time cmd1 | cmd2 | cmd3

如果想分别知道管道里每个命令的时间,用 \time:

# 需要在子shell里分别统计
time (cmd1 | cmd2 | cmd3)

更精确的办法是各段分别计时。

坑2:缓存影响测试结果

第二次运行同一个命令,结果往往比第一次快,因为有磁盘缓存。

# 第一次:从磁盘读文件
time wc -l huge_file.txt   # real: 5.2s

# 第二次:文件已经在缓存里了
time wc -l huge_file.txt   # real: 0.8s

跑性能测试时,先清缓存看看"冷启动"速度:

# 清除页面缓存(需要root)
sudo sh -c 'sync && echo 3 > /proc/sys/vm/drop_caches'

# 再跑测试
time ./script.sh

或者反过来,热数据场景就预热一下:

# 先预热一次
./script.sh > /dev/null

# 真正计时
time ./script.sh > /dev/null

坑3:脚本本身的开销干扰

如果脚本本身只有一两行,time 测出来的时间主要花在 shell 启动上:

# 这样测一个 echo,大部分时间是启动 shell 本身
time bash -c 'echo hello'
# real: 0m0.004s
# 其实 echo 本身不到 0.001s

所以对于特别快的命令,多跑几次看平均更准确。

坑4:user + sys 大于 real

遇到这种情况,说明你的程序在多核CPU上并行跑。不是bug,是特性:

# 并行压缩,多个CPU同时干活
time gzip largefile.tar
# user 2.0s + sys 0.5s = 2.5s
# real 可能只有 1.5s(两个核分担了)

实战场景(重点!结合真实运维场景)

场景1:数据库备份脚本性能诊断

有次发现凌晨备份脚本越来越慢,用 time 来排查:

# 先对整个脚本计时
time ./backup_db.sh

# 输出
# real: 18m32s
# user: 2m15s
# sys: 1m40s

real 远大于 user+sys,说明时间花在等IO上。分段排查:

# 压缩阶段
time gzip /tmp/db_dump.sql

# 传输阶段(最可疑)
time scp /tmp/db_dump.sql.gz backup-server:/backup/

果然,传输阶段耗时15分钟。方案改成 rsync 增量传输,并把压缩和传输改为并行跑,总时间从18分钟降到4分钟。

场景2:代码性能对比

哪个版本快?用 time 说话:

# 方案A:用 find
time find /data -name "*.txt" -exec grep "error" {} \; > /dev/null

# 方案B:用 grep -r
time grep -r "error" /data/*.txt > /dev/null

实际测试下来,grep -r 通常比 find+exec 快不少。用数据说话,而不是靠猜。

场景3:监控脚本执行耗时

脚本跑得慢没关系,但突然变慢就要报警。可以写个检测:

#!/bin/bash
# 监控备份脚本耗时,超过阈值报警
start=$(date +%s)
./backup.sh
end=$(date +%s)
duration=$((end - start))

# 如果超过10分钟(600秒),发报警
if [ $duration -gt 600 ]; then
    echo "WARNING: 备份耗时 ${duration}s,超过阈值!" | mail -s "备份超时告警" admin@example.com
fi

场景4:用 time 做简单的基准测试

服务器性能对比:

# 计算能力测试(计算圆周率1万位)
time echo "scale=10000; 4*a(1)" | bc -l

# IO能力测试
time dd if=/dev/zero of=/tmp/test bs=1M count=1024

# 网络延迟测试
time curl -s -o /dev/null https://api.example.com/health

今日作业

写一个简单的脚本,执行以下操作,并用 time 统计时间:

#!/bin/bash
# 我的测试脚本
echo "开始测试..."
# 生成1000个0-999的随机数
for i in {1..1000}; do
    echo $RANDOM
done > /tmp/random.txt
# 排序并去重
sort -n /tmp/random.txt | uniq > /tmp/sorted.txt
# 统计行数
wc -l /tmp/sorted.txt
echo "完成"

要求:
1. 保存为 /tmp/test.sh 并加执行权限
2. 用 time 运行它
3. 运行3次,记录下来每次的 real、user、sys
4. 分析为什么 user 和 sys 加起来不等于 real
5. 试着改成 /usr/bin/time -v 看看详细输出

做完后,把3次的结果和你的分析写下来。

E N D

运维少年 · 科技观察

最新文章

随机文章