问题描述
先来看出问题的脚本:
target=/dev/nvme1n1 # 实际为动态获得
sed -i "s/^filename=.*/filename=$target/" ./ebs_*.fio
脚本中用某种方法动态地获得了一个块设备,它是一个带有/
的路径。接下来脚本需要将这个“块设备”配置到./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
如果需要newstring
中包含特殊字符需要用\
进行转义。那么脚本中的sed
语句应该写为:
sed -i 's/^filename=.*/filename=\/dev\/nvme1n1/' ./ebs_*.fio
但是,脚本用到的块设备是动态获取的,因此只能用变量去做替换:
sed -i "s/^filename=.*/filename=$target/" ./ebs_*.fio
我们不便对$target
变量的内容(/dev/nvme1n1
)进行转义,因此变量中的/
就会与sed
语句的语法冲突,从而产生错误。
解决办法
通过学习sed
命令,我发现它有一个很好的设计,就是命令的语法非常灵活。当你不方便转义字符串中/
的时候,sed
命令支持自定义语法的分隔符,让你可以避开这个问题。
用户自定义语法的分隔符很简单,就是在s
的后面紧跟自定义的分隔符即可,比如?
等等……
$ cat file
aaaaaa
bbbbbb
cccccc
$ sed 's/bb/ee/' file
aaaaaa
eebbbb
cccccc
$ sed 's%bb%ee%' file
aaaaaa
eebbbb
cccccc
$ sed 's$bb$ee$' file
aaaaaa
eebbbb
cccccc
$ sed 's@bb@ee@' file
aaaaaa
eebbbb
cccccc
$ sed 's?bb?ee?' file
aaaaaa
eebbbb
cccccc
$ sed 's?bb?ee?g' file
aaaaaa
eeeeee
cccccc
因此,只要稍微变通一下,把脚本改为下面的样子,就可以顺利执行:
target=/dev/nvme1n1 # 实际为动态获得
sed -i "s?^filename=.*?filename=$target?" ./ebs_*.fio
另一种方法
上面的方法自然是最直接且简便的,还有一种比较容易想到的,可以算是一种“曲线救国”的方法:
target=/dev/nvme1n1 # 实际为动态获得
target_sed=$(echo $target |sed -e 's/\//\\\//g')
sed -i "s/^filename=.*/filename=${target_sed}/" ./ebs_*.fio
不再过多解释。