Home > Back-end >  bash dealing with multiple quotation
bash dealing with multiple quotation

Time:11-05

From a bash terminal, I want to log-in to a computing node and execute some command, for example, I do this to check the running jobs

ssh -t node1 'top -bn1 | grep R | grep -v top'

This logins to node1 and runs the top command and grep desired output.
Now I want to check the memory load of the node. I have taken this code from here https://unix.stackexchange.com/questions/119126/command-to-display-memory-usage-disk-usage-and-cpu-load

free -m | awk 'NR==2{printf "Memory Usage: %s/%sMB (%.2f%%)\n", $3,$2,$3*100/$2 }'

Now, I can not just do

ssh -t node1 '<above command>'

as there are multiple quote that will affect the command. Is there a way to execute the above command?

CodePudding user response:

For instance with

ssh -t node1 'free -m | '"awk 'NR==2"'{printf "Memory Usage: %s/%sMB (%.2f%%)\n", $3,$2,$3*100/$2 }'

The idea here is to put the string together in pieces. When you have a sequence containing a single quote, wrap it into double quotes. The parts containing a variable expansion or a double quote gets wrapped between single quotes.

CodePudding user response:

You can use here-doc to avoid quoting issues :

ssh node1 << 'EOF'
free -m | awk 'NR==2{printf "Memory Usage: %s/%sMB (%.2f%%)\n", $3,$2,$3*100/$2 }'
EOF

Notice quotes around EOF so as to pass content as is.

We don't usually use -t of ssh if we just run commands directly

CodePudding user response:

Your command contains single quotes, so you cannot pass this command to ssh as a single-quoted string (single quotes cannot be escaped within single quotes).

Therefore, your options are as follows:

  1. Either remove the single quotes from the command and change it so that it uses only double quotes.
  2. ...or pass the command to ssh in double quotes (ssh -t node1 "<above command>"). You will then need to escape special characters in <above command> (like $ or other double quotes) to prevent local expansion.

In your particular case, you can also work around this issue by not running awk on the remote side, but running it locally instead. So this will work:

ssh -t node1 'free -m' | awk 'NR==2{printf "Memory Usage: %s/%sMB (%.2f%%)\n", $3,$2,$3*100/$2 }'

In many cases this leads to a simpler and more readable solution. (However, you need to be aware that if you process remote data locally, then all remote data is transferred through ssh first -- so don't do this when you need to grep tons of data).

CodePudding user response:

Is there a way to execute the above command?

First define a function with the commands you want to execute. Write normal well-formatted readable code, just in a function. Remember to use shellcheck to check for common mistakes.

f() {
    free -m |
    awk '
        NR==2 {
         printf "Memory Usage: %s/%sMB (%.2f%%)\n", $3, $2, $3 * 100 / $2 
       }
    '
}

Then you will need a simple small function escaping function, that I also posted here:

sshqfunc() { echo "bash -c $(printf "%q" "$(declare -f "$1"); $1 \"\$@\"")"; };

Then just:

ssh -t node1 "$(sshqfunc f)"
  • Related