shell quote怎样少走弯路
今天搞了好久的shell脚本,一直卡在quote这部分
以前学过shell的quote规则,现在忘的一干二净,导致今天写shell脚本费好大力气。
结果忙活半天,才终于发现少走弯路的“秘诀”
需求是这样:
cmd1 "cmd2"
或
cmd1 'cmd2'
cmd2会由cmd1执行。
其中,
cmd2里 使用了双引号、单引号。
如果cmd2执行出错,cmd1不会输出错误。(调试)
总结
先总结一下步骤:能理解就不用看后面了
1. 编写、调试 原命令(没被双引号括起来的版本),原命令无问题后,下一步。
2. 用双引号括起来
3. 进行转义(添加反斜杠)
4. 使用echo输出 添加转义后的命令: echo "cmdXXX"
5. 对比输出与原命令的不同,再对转义进行修改,再echo
6. 输出与原命令相同时,转义的工作完成
单独编写cmd2的时候,在终端里测试的好好的,
后来发现还要再用引号括起来,感觉难度立马高了不少(实际上难度并没有增加)
实例:
1. 用ip命令获取接口列表
➜ ~ ip link |awk -F': ' '/^[0-9]+:/{print gensub("@.*$", "", 1,$2)};/altname/{print gensub(" altname ", "",1);}' lo enp7s0 eth0 wlan0 wlp8s0 docker0
(eth0 是 enp7s0的altname,wlp8s0是wlan0的altname)
之所以想把接口的altname 也当作接口列出来,主要是因为altname也能用于命令中,比如
➜ ~ ip link show dev wlp8s0 #这里使用的是altname 4: wlan0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DORMANT group default qlen 1000 link/ether xx:xx:xx:xx:xx:xx brd ff:ff:ff:ff:ff:ff altname wlp8s0 #信我啊
2. 由_call_program命令执行上面这个命令
➜ ~ _call_program interfaces "ip link |awk -F': ' '/^[0-9]+:/{print gensub("@.*$", "", 1,$2)};/altname/{print gensub(" altname ", "",1);}'"
zsh: no matches found: ip link |awk -F': ' '/^[0-9]+:/{print gensub(@.*$, , 1,)};/altname/{print gensub(
直接用双引号或单引号引起来肯定是不行的
需要对命令里的单/双引号做转义:加反斜杠
:就是这么简单,只加反斜杠就可以了。
3. 最终的版本(省略了_call_..方便对比)
➜ ~ ... "ip link |awk -F': ' '/^[0-9]+:/{print gensub(\"@.*$\", \"\", 1,\$2)};/altname/{print gensub(\" altname \", \"\",1);}'"
对比一下没有最外层双引号的版本
➜ ~ ip link |awk -F': ' '/^[0-9]+:/{print gensub("@.*$", "", 1,$2)};/altname/{print gensub(" altname ", "",1);}'
不同之处已经用黄色背景标注出来了。可以发现,无非是双引号前加上/。
但
以上这种简单的情况只适用于 原命令用单引号括双引号
对于原命令只有单引号的情况,只需要给$前加上\即可
对于其他情况,会复杂很多。暂时先不写了。
quote,需要考虑哪些因素:
1. 原命令要用 单引号括双引号的方式编写,
:这个容易理解,单引号括双引号的方式,可以减少原命令中的转义,也减少括起来时需要添加的转义
2. 变量替换:shell和awk都可用 $xxx的格式引用变量,要防止转义后原本要被awk使用的$xxx先被shell替换了
:用斜杠对$xxx转义 \$xxx
3. 注意,单引号内不支持对单引号转义 (与其他编程语言不同)
:echo 'he\'s'↲
quote>
:整个命令都只用 单引号+\,可能很困难。
说了这么多,到底怎么走弯路?
首先遵守以上几条规则
然后是最重要的一条:
用echo把命令输出到终端,然后对比输出的命令与原命令
因为 _call_program不输出错误,所以命令到底哪儿错了也不知道,但是用echo "cmd",就能知道_call_program到底要执行什么命令(即经过转义之后的命令)
比如
➜ ~ echo "ip link |awk -F': ' '/^[0-9]+:/{print gensub(\"@.*$\", \"\", 1,\$2)};/altname/{print gensub(\" altname \", \"\",1);}'"
ip link |awk -F': ' '/^[0-9]+:/{print gensub("@.*$", "", 1,$2)};/altname/{print gensub(" altname ", "",1);}' 经过转义后的命令,具体哪里错了对比一下原命令与此命令,不一样的地方就是有问题的地方
➜ ~ ip link |awk -F': ' '/^[0-9]+:/{print gensub("@.*$", "", 1,$2)};/altname/{print gensub(" altname ", "",1);}' 执行
lo
enp7s0
eth0
wlan0
wlp8s0
docker0