13

I need to write a string to a file on a remote host using python's paramiko module. I've been trialing various methods of redirecting input but with no success.

The localstring in the below code snippet is populated with the result of a cat command

stdin, stdout, stderr = hydra.exec_command('cat /file.txt')
localstring = stdout.read()
manipulate(localstring)
hydra.exec_command('cat > newfile.txt\n' + localstring + '\n')

I seem to have my script hang or receive an EOF error or not have the resulting string appear in the file at all. Note that the file has multiple lines.

user2451085
  • 131
  • 1
  • 1
  • 4

3 Answers3

15

You could also use the ftp capability:

import paramiko

ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect('example.com', username='username', password='password')

ftp = ssh.open_sftp()
file=ftp.file('remote file name', "a", -1)
file.write('Hello World!\n')
file.flush()
ftp.close()
ssh.close()
Nigel
  • 151
  • 1
  • 3
5

As a variation of @Nigel's answer, you can use putfo, which (by default) additionally verifies the file's size after writing and makes sure the file is properly closed.

import paramiko
from io import BytesIO

ssh = paramiko.SSHClient()
ssh.connect(…)

ftp = ssh.open_sftp()
ftp.putfo(BytesIO(localstring.encode()), 'newfile.txt')

ftp.close()
ssh.close()

[Edit] Do not use StringIO as it reports the number of characters as length, while paramiko uses stat to report the number of bytes of the file on the remote system. If they mismatch, the transfer fails.

Caesar
  • 6,733
  • 4
  • 38
  • 44
-1

Just an alternative and easy way. Not necessarily the best one.
I had a complex code in hand and didn't want to rewrite the SSH module with SCP stuff... so I did the following

file_path = ""
file_content = "" # Can be multi - line 
command = f"""
    cat << EOL > {file_path}
    {file_content}
    EOL
"""
stdin, stdout, stderr = client.exec_command(command)
Jay Joshi
  • 1,402
  • 1
  • 13
  • 32
  • 1
    Of course, this works only if `file_content` doesn't contain a line with only the string "EOL"; personally, I would avoid that by generating a new, unpredictable UUID and using that as the sigil instead of hardcoding `EOL`. Also, the indentation right now makes it not work _at all_: it'll add spaces inside the remote file before its first line, and the `EOL` isn't recognized because it's indented. – Charles Duffy Nov 21 '22 at 12:55