如何在ssh / 远程Bash命令中转义单引号字符

38
我正在构建一组小脚本,用于远程启动、停止和检查进程的状态。这些脚本中的“停止”操作应该查找并终止一个进程。因此,我执行以下操作:
ssh deploy@hera 'kill -9 `ps -ef | grep MapReduceNode | grep -v "grep" | awk -F " " '{print $2}' | head -n 1`'

问题在于AWK标记化步骤需要使用单引号,而这与通过ssh执行远程命令时使用的单引号冲突。如何转义这些单引号?

尝试 > ssh deploy@hera 'kill -9 `ps -ef | grep MapReduceNode | grep -v "grep" | awk -F " " "'"{print $2}"'" | head -n 1`' - RedX
@RedX 这个有效,谢谢!请将其用作答案,我会接受的。 - SkyWalker
相关链接:*如何在单引号字符串中转义单引号* - Peter Mortensen
类似的(更旧,但回答较少):*转义单引号ssh远程命令* - Peter Mortensen
5个回答

38

使用

ssh deploy@hera 'kill -9 `ps -ef | grep MapReduceNode | grep -v "grep" | awk -F " " '"'"'{print $2}'"'"' | head -n 1`'

说明:

ssh deploy@hera 'kill -9 `ps -ef | grep MapReduceNode | grep -v "grep" | awk -F " " '"'"'{print $2}'"'"' | head -n 1`'
                >                               1                                   <>2<>    3     <>4<>      5      <

1) 带有命令开头的第一个字符串:'kill -9 `ps -ef | grep MapReduceNode | grep -v "grep" | awk -F " " '

2) 只有单引号的第二个字符串:"'"

3) 带有打印命令的第三个字符串:'{print $2}'

4) 另一个单引号的第四个字符串:"'"

5) 带有剩余命令的第五个字符串:' | head -n 1`'


4
解释比仅仅解决这个具体问题更好。 - LukeFilewalker

15

这不是 sshawk 处理引号,而是 shell(它们必须保留以防止 shell 处理其他特殊字符,例如 $)。嵌套引号不受支持(尽管其他结构,如 $(),可以在包含引号的同时进行嵌套),因此您需要单独转义单引号。以下是几种方法:

$ echo 'Don'"'"'t mess with this apostrophe!'
Don't mess with this apostrophe!
$ echo 'Don'\''t mess with this apostrophe!'
Don't mess with this apostrophe!

11

在其他答案中没有提到的还有两个选项。为了演示目的,我保留了grep/grep/awk/head管道,尽管(如rici的答案中所暗示的那样),它可以缩减为类似以下内容:

awk -F ' ' '/MapReduceNod[e]/ { print $2; exit }'
  1. Using double quotes for the whole ssh command:

     ssh deploy@hera "kill -9 \$(ps -ef |
     grep MapReduceNode | grep -v \"grep\" | awk -F ' ' '{print \$2}' | head -n 1)"
    

    Notice that I can use single quotes in the command now, but I have to escape other things I don't want expanded yet: \$() (which I've used instead of backticks), double quotes \", and print \$2.

  2. A here-doc with quoted delimiter:

     ssh -T deploy@hera <<'EOF'
     kill -9 $(ps -ef | grep MapReduceNode | grep -v 'grep' |
     awk -F ' ' '{print $2}' | head -n 1)
     EOF
    

    The -T prevents ssh from complaining about not allocating a pseudo-terminal.

    The here-doc with quoted delimiter is extra nice because its contents don't have to be modified at all with respect to escaping things, and it can contain single quotes.


2
Here-doc示例非常有用,提醒引用EOF会影响bash是否评估以下文本。 - WeakPointer

8

单引号不能出现在单引号字符串中。然而,这并不重要,因为一个参数可以有多个带引号的部分(只要没有未用引号括起来的空格或其他自限定字符)。

例如:

ssh deploy@hera 'kill -9 `ps -ef |
                 grep MapReduceNode |
                 grep -v "grep" |
                 awk -F " " '\''{print $2}'\'" |
                 head -n 1`"

然而,该命令行非常笨重。如果可能的话,你应该使用pkill实用程序,这将把所有内容简化为ssh deploy@hera 'pkill -SIGKILL MapReduceNode'
否则,您可以在单个awk调用中执行所有字符串操作(未经测试,但我认为它会起作用):
ssh deploy@hera 'ps -ef |
                 awk "/[M]apReduceNode/{system(\"kill -9 \"$2)}"'

与原始版本不同,这将杀死所有的MapReduceNode任务而不是任意的第一个任务。如果您确实只想在一个任务中执行,将; exit添加到awk操作中即可。


0
另一个例子是处理单引号或双引号,因为例如对我来说,我需要解释和变量替换。如果我想要创建一个函数来显示消息到我女友的 macOS 上,我可以这样做:
ssh womanLptp "osascript -e 'tell app \"System Events\" to display dialog \"${1}\"'"

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接