GhostCat
GhostCat

Reputation: 140505

How to transport " for remote execution?

I am working on some python tooling to configure remote systems. One configuration step requires to replace values in a text file. I thought to do a simple

ssh [email protected] sed -i -e s/property=\"true\"/property=\"false\"/g the.remote.file

But well, it doesnt work.

I tried shlex + subprocess, I tried pxssh (which I would like to avoid anyway, as it is much slower compared to a ssh call via subprocess). It seems that whatever I try, the quotes around true/false are removed at some level. But the quotes are in that file on the remote system; so sed will do nothing if the quotes are not showing up in the call that is executed on the remote system.

I could get it working when invoking ssh directly from my bash; but from within python ... no chance. In the end, I put that string into a file; use scp to copy that file to the remote system; and then I ran it from there using ssh again.

But still wondering - is there a clean, straight forward way to do this "just using python"?

(Note: I am looking for solutions that work out of box; not something that requires my users to install other "third party" modules like paramiko.)

Update:

This is ... crazy. I just ran this code (which I think looks exactly like the code that is given in the answer --- I added the .format() afterwards to easily hide username/ip from the posted text):

cmd_str= 'ssh {} "cat ~/prop"'.format(target)
subprocess.call(cmd_str, shell=True)

cmd_str= 'ssh {} "sed -i -e s/property=\"true\"/property=\"false\"/g ~/prop"'.format(target)
subprocess.call(cmd_str, shell=True)

cmd_str= 'ssh {} "cat ~/prop"'.format(target)
subprocess.call(cmd_str, shell=True)`

and I get:

property="true"
property="true"

Could that depend on the configuration of the sshd on the remote system?

Upvotes: 1

Views: 87

Answers (1)

artemdevel
artemdevel

Reputation: 641

Everything worked fine for me, I tried this:

In [1]: import subprocess
In [2]: cmd = 'ssh [email protected] "sed -i -e s/property=\"true\"/property=\"false\"/g ~/cfg.cfg"'
In [3]: subprocess.call(cmd, shell=True)
Out[3]: 0

and as shell=True can be unsafe I also tried this:

In [4]: parts = ['ssh', '[email protected]', '`sed -i -e s/property=\"true\"/property=\"false\"/g ~/cfg.cfg`']
In [5]: subprocess.call(parts)
Out[5]: 0

In both cases my file was changed.

UPDATE: shlex output

In [27]: shlex.split(cmd)
Out[27]: 
['ssh',
 '[email protected]',
 'sed -i -e s/property=true/property=false/g ~/cfg.cfg']

Also note, In the first case I use double quotes for the remote command but in the second case I use back quotes for the command.

UPDATE 2:

In [9]: subprocess.call(['ssh', '[email protected]', 'cat ~/cfg.cfg'])
property="true"
Out[9]: 0
In [10]: subprocess.call(['ssh', '[email protected]', '`sed -i -e s/property=\\"true\\"/property=\\"false\\"/g ~/cfg.cfg`'])
Out[10]: 0
In [11]: subprocess.call(['ssh', '[email protected]', 'cat ~/cfg.cfg'])
property="false"
Out[11]: 0

UPDATE 3:

In [22]: cmd = 'ssh [email protected] \'`sed -i -e s/property=\\"true\\"/property=\\"false\\"/g ~/cfg.cfg`\''
In [23]: subprocess.call(['ssh', '[email protected]', 'cat ~/cfg.cfg'])
property="true"
Out[23]: 0
In [24]: subprocess.call(cmd, shell=True)
Out[24]: 0
In [25]: subprocess.call(['ssh', '[email protected]', 'cat ~/cfg.cfg'])
property="false"
Out[25]: 0
In [26]: shlex.split(cmd)
Out[26]: 
['ssh',
 '[email protected]',
 '`sed -i -e s/property=\\"true\\"/property=\\"false\\"/g ~/cfg.cfg`']

Upvotes: 1

Related Questions