03 May 2022

How to execute multiple commands directly as ssh argument?

 Perhaps sometimes you need to do this:

ssh user@10.1.2.3 ls

It is easy understand the above: run ls after getting into 10.1.2.3 via ssh. Piece a cake

Now comes something that I haven't thought it would happen. For simplicity, I would just paste the entire script (the actual script that I worked on was more complex, so I made simpler one)

#!/bin/bash

ssh root@192.168.56.101 '

if [[ '"x$1"' == "x-a" ]];

then

        echo "-a is here"

else

        echo "not -a"

fi

'

 The above script, let's name it test.sh , just receive one parameter (I don't bother to do number of parameter check etc, btw). Let's assume 192.168.56.101 is a machine or VM somewhere.

At the first encounter, you might wonder, "Good Lord, why write these multi lines command passed into ssh? Why not put them as script at the remote machine and invoke it via ssh?"  I also came to same initial conclusion at first, so basically this is the situation where you are unable to prepare the script at the remote machine first. 

Let's move to the first question "Why single quote? Would double quotes yield the same result?"

The answer is: you want to make sure that the strings of commands are not interpreted locally. But instead pass as is to ssh, let them run remotely, then let remote shell interpret and run them according to remote shell situation. (Better explanation is: actually you can do it with double quotes and make it simpler, but then you need to juggle with escaping etc to avoid local expansion).

Oh and don't forget to put the another single quote after the last command! This kind of quote enclosing is easy to miss by the way (another reason why I hate this! But for the sake of learning, let's continue)

Second, what is this '"x$1"' == "x-a" ? Am I drunk or something??

Fortunately not (otherwise I wouldn't be able to write this blog, would I?) OK, about the x$1, it is a common trick to do string comparison. In ideal world, $1 (the first parameter passed to script) shouldn't be empty string. But say it is. Thus $1 would also be empty string. Then:

  == -a

  (yeah there is a space at front, you just don't see it). This is broken comparison. Thus to avoid, let's put any random character. I usually pick x. So now if someone run it with empty parameter, after expansion, it would be:

x == x-a

Obviously the result is wrong. but now the comparison is valid. Got the idea?

And.... the last one, why '"x$1"' Remember that we start passing commands to ssh with single quote. But $1 as positional parameter is only recognized by local environment (not the target ssh machine). So it has to be expanded HERE, NOW, LOCAL (I hope I stress it enough). Thus, let's stop the single quote enclosing. Hence the left most '

Then put the first " (double quote). You could abandon it btw, but it is ALWAYS a GOOD practice to enclose a variable referencing with double quote. Then the dollar sign and variable name ($1) to refer to first parameter of the script.

Another double quote. Then we start new single quote enclosing, otherwise, the quotes are not paired:

' (begin) --> ' (end) $1 '(start again) --> '(final)

This might sounds daunting at first, but after few rounds of practice, I am quite sure you will get the idea.

Conclusion: bash is full of tricks, maybe you think you already know all of them, but later you stumble for error and learn new ones (like I did). Well, life is full of surprise. Unless you stop learning, then you will be one step wiser and smarter.

Happy hacking!

regards,

Mulyadi


How to execute multiple commands directly as ssh argument?

 Perhaps sometimes you need to do this: ssh user@10.1.2.3 ls It is easy understand the above: run ls after getting into 10.1.2.3 via ssh. Pi...