作者:史晨
创建日期:2018年05月15日
最后修改:2019年03月05日
解决方案
转义会降低可读性,只需用其他特殊字符作为sed
表达式的“分隔符”(取代默认的/
)即可。
例如:sed 's#CONFIG#/etc/app/config.yaml#g'
,使用#
代替/
从而避免大量转义。
你可以尝试一下echo aabbccdd | sed 's#aa#bb#g' | sed 's?bb?cc?g' | sed 's@cc@dd@g' | sed 's%dd%ee%g'
,可谓总有一款适合你……
这是sed
命令方便用户的一个特性,vi
中的:s
也同样支持,灵活使用,相当方便!
更多细节
问题描述
出现问题的脚本:
user_device=/dev/nvme1n1
......
sed -i "s/^filename=.*/filename=$user_device/" ./ebs_*.fio
- 1
- 2
- 3
脚本获取用户指定的块设备(一个带有/
的路径),并将它写入./ebs_*.fio
文件中。
执行脚本报错sed: -e expression #1, char 26: unknown option to 's'
,使用bash -x
调试脚本发现sed
命令所在行被解析为:
+ sed -i 's/^filename=.*/filename=/dev/nvme1n1/' ./ebs_ssd_randread.fio ./ebs_ssd_randwrite.fio
问题分析
我们使用sed
来替换文本文件中的指定字符串,它的一般语法如下:
sed 's/oldstring/newstring/' file
如果string
中包含特殊字符,则需要用\
进行转义。那么脚本中的sed
语句应该写为:
sed -i 's/^filename=.*/filename=\/dev\/nvme1n1/' ./ebs_*.fio
但是,脚本中的块设备是一个变量:
sed -i "s/^filename=.*/filename=$user_device/" ./ebs_*.fio
我们不便对$user_device
的内容(/dev/nvme1n1
)进行转义,因此变量中的/
就会与sed
表达式的语法冲突,从而产生错误。
看到的一种解决方法
user_device=/dev/nvme1n1
......
user_device_sed=$(echo $user_device |sed -e 's/\//\\\//g')
sed -i "s/^filename=.*/filename=${user_device_sed}/" ./ebs_*.fio
- 1
- 2
- 3
- 4
思路很简单,将路径转换成带有转义符的sed
可以使用的字符串。
更好的解决方案
通过学习sed
命令,我发现它有一个很好的设计,就是表达式本身的语法非常灵活。当用户不方便转义字符串中的特殊字符(如/
)时,sed
支持使用自定义分隔符,让用户避开冲突。
用户自定义语法的分隔符很简单,就是在s
的后面紧跟自定义的分隔符即可。经过尝试,自定义的分隔符可以为%
、$
、@
、?
、#
等等不会冲突的字符……大家可以简单尝试一下就可以掌握,非常方便!
echo aabbccdd | sed 's#aa#bb#g' | sed 's?bb?cc?g' | sed 's@cc@dd@g' | sed 's%dd%ee%g'
可谓总有一款适合你……
因此,只要把脚本改为下面的样子,就可以顺利执行,非常简单:
user_device=/dev/nvme1n1
......
sed -i "s#^filename=.*#filename=$user_device#" ./ebs_*.fio
- 1
- 2
- 3
参考文献
- https://www.cnblogs.com/linux-wangkun/p/5745584.html
- https://blog.csdn.net/loveaborn/article/details/17269645
</div>
<link href="https://csdnimg.cn/release/phoenix/mdeditor/markdown_views-e9f16cbbc2.css" rel="stylesheet">
</div>