改代码时经常遇到这种需求:把整个工程里某个旧字符串全部替换成新的,比如交叉编译工具链从 arm-none-linux-gnueabi- 换成 arm-linux-。手工逐个文件改既慢又容易漏,用 sed 配合 grep 一条命令就能搞定。
核心命令
以下命令把当前目录(含子目录)下代码中所有的 arm-none-linux-gnueabi 相关字符串替换掉:
sed -i 's/arm-none-linux-gnueabi-g++/arm-linux-g++\ -O2\ -I\$\(INC\)\ -I\$\(INCSYS\)\ -static/g' `grep arm-none-linux-gnueabi-g++ . -rl`
sed -i 's/arm-none-linux-gnueabi-gcc/arm-linux-gcc\ -static/g' `grep arm-none-linux-gnueabi-gcc . -rl`
sed -i 's/arm-none-linux-gnueabi/arm-linux/g' `grep arm-none-linux-gnueabi . -rl`
同样的套路可以替换任意字符串,比如设备节点名和路径:
sed -i 's/nand1-2/mtd/g' `grep nand1-2 . -rl`
sed -i 's/\/opt\/RTU\/data\/etc/\/mnt\/mtd/g' `grep /opt/RTU/data/etc . -rl`
注意第三条命令覆盖了前两条的范围,所以执行顺序上要先替换更长的、更具体的字符串(-g++、-gcc),最后再替换公共前缀,否则前缀先被替换掉,后面就匹配不到了。
配合原理
这条命令能工作,靠的是两个工具各司其职:
grep -rl:-r递归搜索当前目录下所有文件,-l表示只列出包含匹配内容的文件名,而不是打印匹配行。- 反引号(
`)做命令替换:把grep输出的文件列表直接作为参数传给sed。 sed -i:-i表示 in-place,直接修改文件内容并写回,而不是把结果打印到屏幕上。
三者串起来就是:grep 找出哪些文件包含目标字符串,sed 负责在这些文件里原地进行替换。
替换路径这类含 / 的字符串时,需要对 / 转义(如 \/opt\/);也可以换用其他分隔符来少打转义,例如 sed -i 's#/opt/RTU/data/etc#/mnt/mtd#g'。
补充:sed 在行首/行尾添加字符
在每行的行首添加字符(比如 HEAD):
sed 's/^/HEAD&/g' test.file
在每行的行尾添加字符(比如 TAIL):
sed 's/\$/&TAIL/g' test.file
补充:用 sed 清理 Windows 换行符
Unix 系统里每行结尾只有换行符 \n;Windows 系统里每行结尾是回车加换行 \r\n。一个直接后果是:Unix 下的文件在 Windows 里打开时所有文字会挤成一行;而 Windows 下的文件在 Unix 里打开时,每行结尾会多出一个 ^M 符号。
这类问题很适合用 sed 预处理,把 \r 去掉:
sed -i 's/\r//' test.file
小结
- 字符串替换交给
sed 's/from/to/g'处理,加-i直接改文件。 - 批量替换的公式:
sed -i 's/旧/新/g' \grep 旧 . -rl``。 - 替换有公共前缀的多个字符串时,先长后短。