38

I'm building a small set of scripts for remotely starting, stopping and checking the status of a process. The stop of these scripts should look for a process and kill it. Therefore I do:

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

The problem here is that the AWK tokenization step needs single quotes and these clash with the single quote utilized for executing the remote command via ssh. How can these single quotes be escaped?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
SkyWalker
  • 13,729
  • 18
  • 91
  • 187
  • Try > ssh deploy@hera 'kill -9 \`ps -ef | grep MapReduceNode | grep -v "grep" | awk -F " " "'"{print $2}"'" | head -n 1\`' – RedX Dec 10 '13 at 15:38
  • @RedX This works thanks! use it as answer and I will accept. – SkyWalker Dec 10 '13 at 15:46
  • Related: *[How to escape single quotes within single quoted strings](https://stackoverflow.com/questions/1250079/)* – Peter Mortensen Aug 16 '23 at 18:41
  • Similar (older, but fewer answers): *[Escape single quotes ssh remote command](https://stackoverflow.com/questions/15567847/escape-single-quotes-ssh-remote-command)* – Peter Mortensen Aug 16 '23 at 18:48

5 Answers5

38

Use

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

Explanation:

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) First string with beginning of command: 'kill -9 `ps -ef | grep MapReduceNode | grep -v "grep" | awk -F " " '

2) Second string with only a single ' char: "'"

3) Third string with the print command: '{print $2}'

4) Fourth string with another single quote: "'"

5) Fifth string with rest of command: ' | head -n 1`'

RedX
  • 14,749
  • 1
  • 53
  • 76
15

This is not ssh or awk handling the quotes, it is the shell (and they are necessary to keep the shell from handling other characters specially, like $). Nesting them is not supported (although other structures, such as $() may nest even while containing quotes), so you'll need to escape the single quotes separately. Here are a couple of methods:

$ 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!
Yann Vernier
  • 15,414
  • 2
  • 28
  • 26
11

There are two more options I don't see mentioned in any of the other answers. I've left the grep/grep/awk/head pipeline intact for demonstration purposes, even though (as alluded to in rici's answer) it could be reduced to something like

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.

Benjamin W.
  • 46,058
  • 19
  • 106
  • 116
  • 1
    The here-doc example is very useful as is the reminder that quoting EOF makes a difference to whether the following text is evaluated or not by bash. – WeakPointer Jul 15 '21 at 13:59
8

You can't include a single quote in a single-quoted string. However, that doesn't matter because a single argument can have more than one quoted segment (as long as there is no unquoted whitespace or other self-delimiting characters.)

For example:

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

However, that command line is very clunky. If possible, you should use the pkill utility, which would reduce all that to ssh deploy@hera 'pkill -SIGKILL MapReduceNode'.

Otherwise you could do all the string manipulation in a single awk invocation (untested, but I think it will work):

ssh deploy@hera 'ps -ef |
                 awk "/[M]apReduceNode/{system(\"kill -9 \"$2)}"'

(unlike the original, this will kill all MapReduceNode tasks rather than some arbitrary first one. If you really want to just do in one task, add ; exit to the awk action.)

rici
  • 234,347
  • 28
  • 237
  • 341
0

Another example is to deal with simple or double quotes because for me for example I needed interpretation and variable replacements. If I want to make a function to display a msg to the macOS of my woman I can do followings:

ssh womanLptp "osascript -e 'tell app \"System Events\" to display dialog \"${1}\"'"
Pipo
  • 4,653
  • 38
  • 47