遠程執行命令之單雙引號、轉義符號問題


環境說明

主機名 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
//與雙引號一致

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM