这个命令是干啥的
cut 和 tr 是两个轻量级的文本处理命令,专门管"切割"和"替换"这两件事。
cut 是按列切数据。给你一个用逗号、冒号、tab 分列的文件,想取第2列和第5列?cut 一行搞定。
tr 是按字符做替换和删除。想把小写转大写?去掉文件中的换行符?把连续多个空格压成一个?tr 就是干这个的。
这俩命令都很"专一",各自只做一件事。但它们配合 awk、sort 这些命令之后,能解决很多日常问题。我写脚本的时候,能用 cut 和 tr 解决的,绝不上 awk,因为语法简单,看代码的人不费劲。
基本用法(3分钟上手)
cut 基础
# 准备一个测试文件
cat > students.txt << 'EOF'
张三,18,北京,计算机
李四,20,上海,数学
王五,19,广州,英语
EOF
# 以逗号为分隔符,提取第1列和第3列
cut -d',' -f1,3 students.txt
输出:
张三,北京
李四,上海
王五,广州
-d 指定分隔符,-f 指定要哪几列(可以写1,3或者1-3或者1,2-4)。
按字符位置切
# 提取每行的第1到第3个字符
cut -c1-3 students.txt
# 提取每行的第5个字符
cut -c5 students.txt
-c 是按字符位置,不是按列。遇到中文也按字符算,好用。
去掉不想要的列
# 取除了第2列以外的所有列(补集)
cut -d',' -f1,3- students.txt
注意 -f3- 表示从第3列到最后一列,这是 cut 的一个常用技巧。
tr 基础
# 小写转大写
echo "hello world" | tr 'a-z' 'A-Z'
# 替换字符
echo "hello world" | tr 'lo' 'LO'
输出:
HELLO WORLD
heLLO wOrLd
注意 tr 是做"字符级"替换,不是"字符串级"替换。tr 'lo' 'LO' 是把 l 替换成 L,o 替换成 O,不是把 "lo" 整体替换成 "LO"。
# 删除指定字符
echo "hello 123 world 456" | tr -d '0-9'
# 压缩连续相同字符
echo "hello world" | tr -s ' '
# 补集(保留字母,删除其他所有字符)
echo "hello 123 world!" | tr -d -c 'a-zA-Z \n'
-s(squeeze)压缩连续的重复字符,最常见的用法是压多个空格成一个。
进阶骚操作
cut 处理 /etc/passwd
# 提取用户名和家目录
cut -d':' -f1,6 /etc/passwd
# 提取用户名和 shell
cut -d':' -f1,7 /etc/passwd | head -5
输出:
root:/root
daemon:/usr/sbin
bin:/bin
sys:/dev
sync:/bin
cut 与管道组合
# 从 ls -l 中提取文件大小(第5列)和文件名(第9列)
ls -lh | tr -s ' ' | cut -d' ' -f5,9
# 提取 IP 地址(从 ifconfig 输出中)
ifconfig eth0 | grep 'inet ' | tr -s ' ' | cut -d' ' -f3
注意 ls -lh 各列之间是多个空格,cut -d' ' 会把连续空格当成多个空列。所以先 tr -s ' ' 把多个空格压缩成一个,再用 cut 才能正确取到列。这个坑我踩过好几次。
tr 做大小写转换
# 把整个文件从小写转大写
tr 'a-z' 'A-Z' < input.txt > output.txt
# 把变量转大写,方便后面做比较
env_name=$(echo "$ENV" | tr 'a-z' 'A-Z')
if [ "$env_name" = "PRODUCTION" ]; then
echo "这是生产环境"
fi
tr 去掉换行符
# 把多行文本合并成一行
cat file.txt | tr '\n' ' '
# 去掉 Windows 的 ^M(CRLF 转 LF)
tr -d '\r' < windows_file.txt > linux_file.txt
"去掉 \r"这条命令我每个月都会用一次,因为总有同事从 Windows 传文件过来导致脚本报错。
tr 去除特殊字符
清洗从网页或者 API 抓下来的文本:
# 去掉所有 HTML 标签的角括号
echo "<p>Hello</p>" | tr -d '<>'
# 去掉不可见控制字符(保留可打印字符)
cat dirty.txt | tr -d -c '[:print:]'
[:print:] 是 POSIX 字符类,代表所有可打印字符。类似的还有 [:space:]、[:digit:]、[:alpha:] 等。
cut 的续行问题
# 注意:cut 是一行一行处理的,不会跨行截取
# 如果你要做"截取文件前10行"的操作:
head -n 10 file.txt | cut -d',' -f1,2
避坑指南
坑1:cut 的分隔符必须是单字符
# 错误!cut 不支持多字符分隔符
cut -d'::' -f1 file.txt
# 正确:用 awk
awk -F'::' '{print $1}' file.txt
cut 的 -d 只能跟一个字符。如果要按 :: 或 => 这种多字符分隔符,老老实实用 awk。
坑2:tr 不支持正则字符串替换
# 错误!tr 不支持把 "hello" 替换成 "world"
echo "hello" | tr 'hello' 'world'
# 正确:用 sed
echo "hello" | sed 's/hello/world/'
tr 是"字符对字符"的映射,不是"字符串对字符串"。很多人(包括最早的我)都以为 tr 能做 sed 那种替换,结果 tr 'hello' 'world' 输出的是 wrrld(h->w, e->o, l->r...)。
坑3:cut 不能重新排序列
# cut 按原文件的列顺序输出,不能重新排列
echo "a,b,c,d" | cut -d',' -f4,2
# 输出 "b,d"(第4列在前,按原顺序)
如果我想输出第4列再输出第2列,用 awk:
echo "a,b,c,d" | awk -F',' '{print $4, $2}'
坑4:cut 处理制表符(tab)
制表符和空格不同。想用 tab 做分隔符:
# 明确指定 tab 做分隔符
cut -d$'\t' -f2 file.tsv
$'\t' 是 bash 的 ANSI-C 转义,能正确地表示 tab 字符。
坑5:tr 只能处理 stdin,不能直接读文件
# 错误!tr 不支持文件名参数
tr 'a-z' 'A-Z' file.txt
# 正确!用重定向
tr 'a-z' 'A-Z' < file.txt
# 或者用管道
cat file.txt | tr 'a-z' 'A-Z'
实战场景(重点!结合真实运维场景)
场景1:处理 CSV 文件提取数据
假设有一个 orders.csv 文件,格式如下:
订单号,客户名,金额,日期,状态
ORD001,张三,299.00,2026-06-01,已支付
ORD002,李四,599.00,2026-06-02,已支付
ORD003,王五,1299.00,2026-06-03,待支付
ORD004,张三,89.00,2026-06-04,已取消
提取特定列:
# 提取订单号和金额,用管道符分隔
cut -d',' -f1,3 orders.csv | tr ',' '|'
# 提取所有已支付订单的客户名和金额
grep "已支付" orders.csv | cut -d',' -f2,3
场景2:去除日志中的控制字符
有时候服务器日志里混着终端控制字符,看都看不清:
# 删除日志中的所有控制字符
cat app.log | tr -d '[:cntrl:]' > clean.log
# 只保留可打印字符、换行和 tab
cat app.log | tr -c -d '[:print:]\t\n' > clean.log
场景3:把逗号分隔的列表转成换行分隔
# 从配置文件里读取用逗号分隔的服务器列表
servers="web01,web02,web03,web04"
echo "$servers" | tr ',' '\n'
输出:
web01
web02
web03
web04
然后可以循环处理:
for server in $(echo "$servers" | tr ',' ' '); do
ssh "$server" "uptime"
done
场景4:处理 ps 输出的列
# 提取 PID 和 COMMAND
ps aux | tr -s ' ' | cut -d' ' -f2,11
# 找出 CPU 使用率最高的进程
ps aux | tr -s ' ' | sort -k3 -rn | head -5 | cut -d' ' -f2,3,11
这个 tr -s ' ' 是处理 ps 输出的必经之路,因为 ps 的列对齐用的是变长空格。
场景5:备份文件时去掉前缀
我经常需要把 backup_20260622.sql 这样的文件名转成 20260622.sql:
# 截取文件名从第8个字符开始
echo "backup_20260622.sql" | cut -c8-
# 或者去掉前缀
echo "backup_20260622.sql" | cut -d'_' -f2-
场景6:统计字母出现频率
这个有点玩票性质,但面试的时候被问过:
# 统计文本中每个字母出现的次数
echo "hello world" | tr -d ' ' | tr -s 'a-z' | fold -w1 | sort | uniq -c | sort -rn
今日作业
有一个文件 data.csv,内容如下:
1001,张三,男,28,北京,开发
1002,李四,女,25,上海,测试
1003,王五,男,30,广州,运维
1004,赵六,女,27,深圳,产品
请完成以下任务:
1. 用 cut 提取第2列(姓名)和第5列(城市),不输出其他列
2. 把输出的分隔符从逗号改成竖线(|)
3. 用 tr 把所有行转成大写输出
4. 结果格式应该是:张三|北京 这样的形式
提示:先 cut 再 tr 再 tr,一条管道搞定。