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

 

posted @ 2020-06-18 19:37  drelo  阅读(1333)  评论(0编辑  收藏  举报