PowerShell 5.1中使用plink执行含双引号的外部命令时的引号转义问题
我完全懂你遇到的麻烦——之前用plink在PowerShell里批量跑Linux命令都顺风顺水,结果一碰到需要用双引号包裹SQL语句的psql命令就卡壳了对吧?这其实是多层引号解析的锅:PowerShell要先解析你的命令字符串,plink再把命令传给远端Linux的shell,最后psql还要处理自己的参数,每一层对引号的规则都不一样,转义得层层对应才行。下面给你几个靠谱的解决办法:
方法1:用单引号构建完整命令(最省心)
PowerShell里单引号包裹的字符串是原样字符串,里面的任何字符(包括双引号)都不会被PowerShell解析转义,直接原封不动传给plink,完全不用纠结转义符:
$Session = 'FIS-P-CDS-PGS-201' $TheCMD = ': ; sudo -iu postgres psql -Xc "SELECT datname FROM pg_database;"' & plink $Session $TheCMD
这样plink会把完整命令传给远端Linux shell,shell能正确识别psql参数里的双引号,执行毫无问题。
方法2:双引号+双引号转义(适合动态拼接命令)
如果你需要在命令里插入PowerShell变量(比如动态生成SQL),就得用双引号包裹命令字符串,这时候内部的双引号需要用两个双引号来转义(这是PowerShell的规则):
$Session = 'FIS-P-CDS-PGS-201' $TheCMD = ": ; sudo -iu postgres psql -Xc ""SELECT datname FROM pg_database;""" & plink $Session $TheCMD
PowerShell会把""解析成一个双引号,传给plink的命令就是正确的格式,远端shell能正常处理。
方法3:改用单引号包裹SQL语句(避开双引号转义)
其实Linux里的命令参数,单引号和双引号都能用来包裹字符串(只要你的SQL里没有单引号),换个思路用单引号包裹SQL,在PowerShell里完全不用转义:
$Session = 'FIS-P-CDS-PGS-201' # 外层用双引号时,内部单引号直接写就行 $TheCMD = ": ; sudo -iu postgres psql -Xc 'SELECT datname FROM pg_database;'" & plink $Session $TheCMD
这个方法最不容易出错,尤其是SQL语句里没有单引号的场景,推荐优先用。
方法4:Here-String(适合复杂多行命令)
如果你的命令很长或者有多行,用PowerShell的Here-String来定义命令字符串,完全不用考虑转义,内容原样保留:
$Session = 'FIS-P-CDS-PGS-201' $TheCMD = @' : ; sudo -iu postgres psql -Xc "SELECT datname FROM pg_database;" '@ & plink $Session $TheCMD
注意Here-String的开头@'和结尾'@要单独占一行,中间的内容完全按你想要的格式写就行。
最后给你排个雷:你原来用"(反引号+双引号)的方式,在PowerShell里确实能解析出双引号,但有时候plink在传递命令给远端shell时,会因为参数拆分的问题导致引号丢失或混乱,所以上面几种方法都比原写法更可靠。
备注:内容来源于stack exchange,提问作者RonJohn

