远程执行命令之单双引号、转义符号问题

环境说明

主机名 IP地址 定义变量  
web01 10.0.0.7 JZL=10.0.0.7  
web02 10.0.0.8 JZL=10.0.0.8  
       


单引号和双引号在ssh命令中的区别

假设本地机器上配置了一个环境变量JZL=10.0.0.7,在本地环境变量配置文件写入该环境变量并显示:

[root@web01 scripts]# tail -1 /root/.bashrc 
JZL=10.0.0.7
[root@web01 scripts]# echo $JZL
10.0.0.7

 

假若我想查看远程机器上的JZL环境变量,则只能使用单引号了;

ssh user@node ' echo $JZL ', 则是' ' 中的$JZL不会被shell解析,而是当做一个字符串,此时参数 echo $JZL 传递给了 ssh;

如果我们使用 ssh user@node ” echo $JZL ",则 shell 首先会解析$JZL,得到它的值,则该命令就变成了 ssh user@node ' echo /opt/jdk ' 了。

 

[root@web01 scripts]# ssh 10.0.0.8 echo $JZL  //不加单双引号变量在本地被解析
10.0.0.7
[root@web01 scripts]# ssh 10.0.0.8 "echo $JZL"  //加了双引号变量在本地被解析
10.0.0.7
[root@web01 scripts]# ssh 10.0.0.8 'echo $JZL'  //加了单引号变量被传入ssh后再进行解析
10.0.0.8
[root@web01 scripts]# ssh 10.0.0.8 "echo \$JZL"  //加了双引号但是变量被转义为普通字符串再传入ssh进行解析
10.0.0.8
[root@web01 scripts]# ssh 10.0.0.8 'echo \$JZL'  //加单引号同时加了转义符号那么变量/$JZL被传入ssh后成了echo $JZL(\代表转义字符),所以显示成了$JZL
$JZL

 

单双引号实验

1. 本次实验有两台机器,分别是web01与web02,web01为远程登录机器,web02作为被远程登录机器

2. web01使用ssh 10.0.0.8 "find /home/web -type d -name "web-*""|tr " " "\n"赋值给old变量

3. web01使用ssh命令远程分别执行ls $old命令

 

//定义的变量
[root@web01 scripts]# ssh 10.0.0.8 "find /home/web -type d -name "web-*""|tr " " "\n"
/home/web/web-2018-10-11-v1.5
/home/web/web-2018-10-11-v1.4
[root@web01 scripts]# old=`ssh 10.0.0.8 "find /home/web -type d -name "web-*""|tr " " "\n"`
[root@web01 scripts]# echo $old
/home/web/web-2018-10-11-v1.5 /home/web/web-2018-10-11-v1.4

//web02的/home/web下的web-2018-10-11-v1.4与web-2018-10-11-v1.5内容
[root@web02 web]# ls web-2018-10-11-v1.4/
v1.4-10.0.0.8
[root@web02 web]# ls web-2018-10-11-v1.5/
v1.5-10.0.0.8

 

1. 分别给要执行的命令加单双引号

 

//脚本
[root@web01 scripts]# cat 1_1.sh 
#!/bin/bash
old="`ssh 10.0.0.8 "find /home/web -type d -name "web-*""|tr " " "\n"`"
echo -e "\033[31m#1.显示本地定义的old变量值 \033[0m"
echo $old

echo -e "\033[31m#2.本地ls显示定义的old的值 \033[0m"
ls $old

echo -e "\033[31m#3.远程ls显示定义的old的值,双引号 \033[0m"
ssh 10.0.0.8 "ls $old"

echo -e "\033[31m#7.远程ls显示定义的old的值,单引号 \033[0m"
ssh 10.0.0.8 'ls $old'

//执行结果
[root@web01 scripts]# sh 1_1.sh 
#1.显示本地定义的old变量值 
/home/web/web-2018-10-11-v1.5 /home/web/web-2018-10-11-v1.4
#2.本地ls显示定义的old的值 
/home/web/web-2018-10-11-v1.4:
v1.4-10.0.0.7

/home/web/web-2018-10-11-v1.5:
v1.5-10.0.0.7
#3.远程ls显示定义的old的值,双引号
v1.5-10.0.0.8
bash: line 1: /home/web/web-2018-10-11-v1.4: is a directory
//由于是双引号,所以old变量会被提前解析,但是/home/web/web-2018-10-11-v1.4与/home/web/web-2018-10-11-v1.5并没有在ls的命令内,/home/web/web-2018-10-11-v1.4单独作为命令,是以显示is a directory //如果想要/home/web/web-2018-10-11-v1.4不作为一个命令的话,那么需要在$old再加一个双引号
#7.远程ls显示定义的old的值,单引号 access.log anaconda-ks.cfg install.log install.log.syslog nginx.sh

 

在old变量加双引号

 

//脚本
[root@web01 scripts]# cat 1_1.sh
#!/bin/bash
old="`ssh 10.0.0.8 "find /home/web -type d -name "web-*""|tr " " "\n"`"
echo -e "\033[31m#1.显示本地定义的old变量值 \033[0m"
echo $old

echo -e "\033[31m#2.本地ls显示定义的old的值 \033[0m"
ls $old

echo -e "\033[31m#3.远程ls显示定义的old的值,双引号 \033[0m"
ssh 10.0.0.8 "ls $old"

echo -e "\033[31m#4.远程ls显示定义的old的值,双引号,old变量也加双引号 \033[0m"
ssh 10.0.0.8 "ls "$old""

echo -e "\033[31m#7.远程ls显示定义的old的值,单引号 \033[0m"
ssh 10.0.0.8 'ls $old'

//执行结果
[root@web01 scripts]# sh 1_1.sh 
#1.显示本地定义的old变量值 
/home/web/web-2018-10-11-v1.5 /home/web/web-2018-10-11-v1.4
#2.本地ls显示定义的old的值 
/home/web/web-2018-10-11-v1.4:
v1.4-10.0.0.7

/home/web/web-2018-10-11-v1.5:
v1.5-10.0.0.7
#3.远程ls显示定义的old的值,双引号 
v1.5-10.0.0.8
bash: line 1: /home/web/web-2018-10-11-v1.4: is a directory
#4.远程ls显示定义的old的值,双引号,old变量也加双引号 
/home/web/web-2018-10-11-v1.4:
v1.4-10.0.0.8

/home/web/web-2018-10-11-v1.5:
v1.5-10.0.0.8
//此时加了双引号后,相当于ls /home/web/web-2018-10-11-v1.5 /home/web/web-2018-10-11-v1.4,old的值被作为了一个整体,所以显示正常 #7.远程ls显示定义的old的值,单引号 access.log anaconda-ks.cfg install.log install.log.syslog nginx.sh

 

小结:
1. ssh远程显示变量的时候,双引号会让变量在本地被先解析,然后再传递给ssh去执行。

2. 单引号会让变量不再本地解析,完整的传递给ssh,但是在远端机器的~/.bashrc文件内并没有old的变量,当然也可以source指定远端机器的环境变量配置文件,所以$old显示为空,那么默认显示的就是远端机器/root目录。

3. 当使用双引号包围了执行命令,然后再用双引号包围了变量后,变量会被视为一个整体传递给其它命令,所以上述输出会正常,变量不加双引号的输出异常。

 

2. 执行的命令加反引号再去传递给ssh

 

//脚本
[root@web01 scripts]# cat 1_2.sh 
#!/bin/bash
old="`ssh 10.0.0.8 "find /home/web -type d -name "web-*""|tr " " "\n"`"

echo -e "\033[31m#4.远程ls显示定义的old的值,双引号加`` \033[0m"
ssh 10.0.0.8 "`ls $old`"

//执行结果
[root@web01 scripts]# sh 1_2.sh
#4.远程ls显示定义的old的值,双引号加 
bash: /home/web/web-2018-10-11-v1.4:: No such file or directory
bash: line 1: v1.4-10.0.0.7: command not found
bash: line 3: /home/web/web-2018-10-11-v1.5:: No such file or directory
bash: line 4: v1.5-10.0.0.7: command not found
//出现该结果的原因是ls $old被先执行了,然后再去传递给ssh,导致传递给ssh的内容全部被当成命令执行...

 

3. 变量前加反引号

 

//脚本
[root@web01 scripts]# vim ceshi.sh
#!/bin/bash
old="`ssh 10.0.0.8 "find /home/web -type d -name "web-*""|tr " " "\n"`"

echo -e "\033[31m#1.远程ls显示定义的old的值,双引号加转义符号 \033[0m"
ssh 10.0.0.8 "ls \$old"

echo -e "\033[31m#2.远程ls显示定义的old的值,单引号加转义符号 \033[0m"
ssh 10.0.0.8 'ls \$old'

//执行结果
[root@web01 scripts]# sh 1_3.sh
1_3.sh: line 1: [root@web01: command not found
#1.远程ls显示定义的old的值,双引号加转义符号 
access.log
anaconda-ks.cfg
install.log
install.log.syslog
nginx.sh
//本来双引号内的变量会被解析的,但是加了转义符号后变量会被原样传递给ssh,传递给ssh后转义符号就完成了任务了,因为是被双引号包围的,\会被系统解析为转义符号
#2.远程ls显示定义的old的值,单引号加转义符号 
ls: cannot access $old: No such file or directory
//为什么会显示No such file or directory,当执行命令被单引号包围时,传递给ssh的命令变为ls \$old,是以会这样显示,是被单引号包围时,\不会被系统解析为转义符号

 

小结:

1. 被执行命令如果被双引号包围,同时变量加转义符号的话,转义符号会被解析,同时变量会在远程机器被解析。

2. 被执行命令如果被单引号包围,同时变量加转义符号的话,转义符号不会被解析,同时转义符号和变量会被作为ls的参数原样执行。

 

4. old变量加单引号

 

//脚本
[root@web01 scripts]# cat 1_4.sh
#!/bin/bash
old="`ssh 10.0.0.8 "find /home/web -type d -name "web-*""|tr " " "\n"`"

echo -e "\033[31m#1.远程ls显示定义的old的值,双引号加变量单引号 \033[0m"
ssh 10.0.0.8 "ls '$old'"

echo -e "\033[31m#2.远程ls显示定义的old的值,单引号加变量单引号 \033[0m"
ssh 10.0.0.8 'ls '$old''

//执行结果加过程
[root@web01 scripts]# sh -x 1_4.sh 
++ tr ' ' '\n'
++ ssh 10.0.0.8 'find /home/web -type d -name web-*'
+ old='/home/web/web-2018-10-11-v1.5
/home/web/web-2018-10-11-v1.4'
+ echo -e '\033[31m#1.远程ls显示定义的old的值,双引号加变量单引号 \033[0m'
#1.远程ls显示定义的old的值,双引号加变量单引号 
+ ssh 10.0.0.8 'ls '\''/home/web/web-2018-10-11-v1.5
/home/web/web-2018-10-11-v1.4'\'''
ls: cannot access /home/web/web-2018-10-11-v1.5
/home/web/web-2018-10-11-v1.4: No such file or directory
+ echo -e '\033[31m#2.远程ls显示定义的old的值,单引号加变量单引号 \033[0m'
//上述结果中,执行的命令变为了ls '\''/home/web/web-2018-10-11-v1.5 /home/web/web-2018-10-11-v1.4'\',在传递给ssh前,''变为了转义符号
//但是没有\/home/web/web-2018-10-11-v1.5 /home/web/web-2018-10-11-v1.4\目录,由于是在双引号内加了单引号,所以会被解析为转义符号\(本处为猜测,实力不足,见谅..)
#2.远程ls显示定义的old的值,单引号加变量单引号 + ssh 10.0.0.8 'ls /home/web/web-2018-10-11-v1.5' /home/web/web-2018-10-11-v1.4 /home/web/web-2018-10-11-v1.4: v1.4-10.0.0.8 /home/web/web-2018-10-11-v1.5: v1.5-10.0.0.8
//为什么单引号加变量的单引号会执行成功目前还是没弄懂,但是sh -x显示的执行细节是与双引号加双引号时一样的,如下所示
//ssh 10.0.0.8 'ls /home/web/web-2018-10-11-v1.5' /home/web/web-2018-10-11-v1.4

 

小结:
1. 双引号加变量单引号会导致单引号被解析为转义符号,导致ls出错

2. 单引号加变量单引号与双引号加变量双引号的结果一致,sh -x执行过程一致

 

总结

1. 最外围是双引号时,那么里面的变量肯定会在本地被解析,注意''会被解析为转义符号\,
2. 最外围是单引号时,那么变量便不回在本地进行解析,而是直接传递给ssh
2. 双引号里面的变量的单双引号均会对ls的结果产生影响,同时转义符号会影响结果
3. 单引号里面的变量的单引号会出现变化(通双引号加变量带双引号),变量会被正常解析并传递给ssh(反正结果是正常的)

 

单双引号加管道的实验

脚本

[root@web01 scripts]# cat ceshi.sh
#!/bin/bash
old="`ssh 10.0.0.8 "find /home/web -type d -name "web-*""|tr " " "\n"`"

echo -e "\033[34m#1.双引号 \033[0m"
ssh 10.0.0.8 "echo $old|xargs ls"

echo -e "\033[34m#2.双引号(变量也带双引号) \033[0m"
ssh 10.0.0.8 "echo "$old"|xargs ls"

echo -e "\033[34m#3.双引号(变量带单引号) \033[0m"
ssh 10.0.0.8 "echo '$old'|xargs ls"

echo -e "\033[34m#4.双引号(变量带转义符号) \033[0m"
ssh 10.0.0.8 "echo \$old|xargs ls"

echo -e "\033[34m#5.单引号 \033[0m"
ssh 10.0.0.8 'echo $old|xargs ls'

echo -e "\033[34m#6.单引号(变量带双引号) \033[0m"
ssh 10.0.0.8 'echo "$old"|xargs ls'

echo -e "\033[34m#7.单引号(变量带单引号) \033[0m"
ssh 10.0.0.8 'echo '$old'|xargs ls'

执行结果

 

[root@web01 scripts]# sh ceshi.sh
#1.双引号 
/home/web/web-2018-10-11-v1.5
bash: line 1: /home/web/web-2018-10-11-v1.4: is a directory
access.log
anaconda-ks.cfg
install.log
install.log.syslog
nginx.sh
//当使用双引号的时候,变量会在本地被解析,然后变量内容在本地执行,所以会出现is a directory提示,并且ls显示的是/root目录 #2.双引号(变量也带双引号) /home/web/web-2018-10-11-v1.4: v1.4-10.0.0.8 /home/web/web-2018-10-11-v1.5: v1.5-10.0.0.8
//当变量带双引号的时候,变量内容会被传递给管道,然后正确执行 #3.双引号(变量带单引号) /home/web/web-2018-10-11-v1.4: v1.4-10.0.0.8 /home/web/web-2018-10-11-v1.5: v1.5-10.0.0.8
//当变量带单引号的时候,变量内容会被传递给管道,然后正确执行 #4.双引号(变量带转义符号) access.log anaconda-ks.cfg install.log install.log.syslog nginx.sh
//双引号加变量被转义时,变量不在本地解析,在传递给ssh后进行解析,远端机器又没old的变量,所以ls显示/root目录 #5.单引号 access.log anaconda-ks.cfg install.log install.log.syslog nginx.sh
//单引号时,变量不在本地解析并传递到ssh,又因为...所以ls显示/root目录 #6.单引号(变量带双引号) access.log anaconda-ks.cfg install.log install.log.syslog nginx.sh
//当执行命令被双引号包围,然后变量带双引号时,变量不在本地解析,ssh接收后在远端机器执行,远端无old变量,所以echo为空,ls默认显示家目录root #7.单引号(变量带单引号) /home/web/web-2018-10-11-v1.4: v1.4-10.0.0.8 /home/web/web-2018-10-11-v1.5: v1.5-10.0.0.8
//与双引号一致

 

posted @ 2019-03-03 16:36  JZLZLZL  阅读(4766)  评论(1编辑  收藏  举报