Home > other >  Execute command with multiple layers of quoting via ssh
Execute command with multiple layers of quoting via ssh

Time:02-10

I want to execute a docker command on a remote server. The problem is I don't know to escape multiple quotes.

ret=$(ssh root@server "docker exec nginx bash -c 'cat /etc/nginx/nginx.conf | grep 'ServerName' | cut -d '|' -f1'")

I get

bash:  -f1: command not found

CodePudding user response:

There's little need to execute so much on the remote host. The file you want to search isn't likely that big: just pipe the entire thing down via ssh to a local awk process:

ret=$(ssh root@server "docker exec nginx cat /etc/nginx/nginx.conf" |
         awk -F'|' '/ServerName/ {print $1}')

CodePudding user response:

EDIT: Originally the answer suggested to use quote, but that only works in interactive shells and according to @JohnKugelman it's better to use printf "%q". So I have replaced quote with printf "%q"

Just wrap your parameter string with N calls to "$(printf "%q" ...)", for N recursive calls .

ssh root@server "docker exec nginx bash -c 'cat /etc/nginx/nginx.conf | grep ServerName | cut -d | -f1'"

How may recursive calls the above line has? I don't wish to set up docker just for the test, so I may have one of the following wrong:

  1. ssh - certainly counts
  2. docker - ??
  3. ngix - ??
  4. bash - certainly counts

If there are four, then you need four calls to "$(printf "%q "str")", don't forget to add all those " marks

ssh root@server docker exec nginx bash -c "$(printf "%q" "$(printf "%q" "$(printf "%q" "$(printf "%q" "cat /etc/nginx/nginx.conf | grep ServerName | cut -d | -f1")")")")"

Explanation: ssh parses the string like bash -c does, stripping one level of quotes. docker and nginx may also each parse the string (or not). Finally, bash -c parses whatever the previous levels have parsed, and removes the final level of quotes. exec does not parse the strings, it simply passes them verbatim to the next level.

Another solution is to put the line, that you want bash to execute, into a script. Then you can simply invoke the script without all this quoting insanity.

#!/bin/bash
< /etc/nginx/nginx.conf grep ServerName | cut -d | -f1

CodePudding user response:

Consider using here-document :

ret="$(ssh root@server << 'EOF'
docker exec nginx bash -c "grep 'ServerName' /etc/nginx/nginx.conf | cut -d '|' -f1"
EOF
)"
echo "$ret"

Or, simpler as suggested by @MichaelVeksler :

ret="$(ssh root@server docker exec -i nginx bash << 'EOF'
grep 'ServerName' /etc/nginx/nginx.conf | cut -d '|' -f1
EOF
)"
echo "$ret"
  • Related