I have two Raspberry Pi's. I am trying to transfer files from one Pi to the other using scp. I am trying to do this through Python because the program that will be transferring files is a python file.
below is the shell script I have for the SCP part (Blurred out the pass and IP):
#!/bin/sh
sshpass -p ######## scp test.txt pi#IP:/home/pi
and below is the Python Script that launches that Shell script.
import subprocess
subprocess.call(['./ssh.sh'])
print("DONE")
For some reason the python script doesnt kick back any errors and hits the print line but the file is not transferred. When i run the scp command outside of python the file transfers just fine. Am I doing something incorrect here?
****EDIT****
I cant even get Subprocess to work with this which is why i ended up using na shell script. Here is my attempt with Subprocess:
import subprocess
subprocess.call("sshpass -p ######## scp test.txt pi#IP:/home/pi")
print"DONE"
Again I get no errors, but the file is not transferred
****EDIT #2****
So I found out that because sshpass is being used, scp isnt prompting me to add the IP to known hosts, as a result the file simply isnt trnasferred at all. I need a way to add this acceptance into the script IE I ge the following if I launch the command without sshpass:
The authenticity of host 'IP (IP)' can't be established.
ECDSA key fingerprint is 13:91:24:8e:6f:21:98:1f:5b:3a:c8:42:7a:88:e9:91.
Are you sure you want to continue connecting (yes/no)?
I want to communicate to pass "yes\n" to this prompt as well as the password afterwards. Is this possible?
For the first query
You can use 'subprocess.popen' to get output(STDOUT) and error(STDERR) for the executed command.
import subprocess
cmd = 'sshpass -p ****** scp dinesh.txt root#256.219.210.135:/root'
p = subprocess.Popen(cmd.split(), stdout=subprocess.PIPE, stderr=subprocess.PIPE)
out, err = p.communicate()
print "Output is ",out
print "Error is ",err
If you execute above code with wrong password, the you will get below output:
[root#centos /]# python code.py
Output is
Error is Permission denied, please try again.
In this case, if the file is successfully transferred, then there is no output.
If you execute command like 'ls -l' then output will be printed.
For your second query (****EDIT #2****)
Options are :
Password less SSH. Check this.
Pexpect
I found a much easier way of tackling all of this
sshpass -p ###### scp -o StrictHostKeyChecking=no test.txt pi#IP:/home/pi
The -o switch allows me to auto store the IP into known hosts thus I do not need to communicate with the shell at all. The interaction from Python to Shell works with that addition; Doing this solely through subprocess also works.
If you don't mind to try other approaches it worth to use SCPClient from scp import.
Related
Consider this python script:
import subprocess
nc = subprocess.Popen(["/bin/bash"], stdin=subprocess.PIPE, text=True)
nc.stdin.write("nc localhost 2222\n")
nc.stdin.write("pwd\n")
When I listen with netcat as nc -lnvp 2222
I successfully connect and send the string pwd nothing more happens of course.
Now I get a non stable php reverse shell(Completely new event) and I connect through netcat successfully. I execute this script to upgrade shell and print current directory. By the way that listener is another Popen instance.
import subprocess
nc = subprocess.Popen(["/bin/bash"], stdin=subprocess.PIPE, text=True)
nc.stdin.write("nc localhost 2222\n")
nc.stdin.write('python3 -c "import pty;pty.spawn(\'/bin/bash\')"\n')
nc.stdin.write('pwd\n')
Now when I execute that python script, I expected the input will go through netcat, get executed in that new bash tty and spawn a stable shell and pass pwd to return current directory. But this script only works upto spawing stable shell and then stdin input doesn't go through nc or something else happens that I'm not aware of.
What's happening here?
Edit: I need to be able to run multiple commands. Using subprocess.communicate(input=<command>) causes deadlock and can't accept stdin.
I have a requirement to get the number of files in the remote server thru Python.I have used the paramiko module and wrote the below script. The linux command when running in the terminal is giving me the desired output whereas when executing from Python is giving me the output as [u'0\n'].Any help is highly appreciated.
#!/usr/local/bin/python
import paramiko
client=paramiko.SSHClient()
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
client.connect('hostname', username='username')
grepCommand="ssh username#hostname 'find /usr/local/somefolder1/somefolder2 -type f -exec ls {} \;'|wc -l"
stdin,stdout,stderr = client.exec_command(grepCommand)
data=stdout.readlines()
print data
client.close()
Since the paramiko library is already making the ssh connection for you, you don't need to execute ssh in your remote command. Also it seems that you are piping the output locally into wc.. something you can't do here because with paramiko you aren't using a local shell. So:
Your grepCommand should be exactly what would work if you were to execute it in a shell on the remote machine.
eg:
find /etc -type f | wc -l
I am currently working on a script where when I launch an EC2 instance, I send a paramiko command to rename the host name. Because this is a custome AMI, I cannot use the AWS Boto3 CLI to do it, so I need to do it via an SSH command.
The problem I am running into, is Paramiko seems to fail at passing my specific command. It will pass other commands just fine, but I am assuming I am running into some sort of limitation of either paramiko or python and cannot seem to troubleshoot it. This is for a RHEL instance, so renaming the Network file is the only way I can think to do this.
If I run the command, as is, through the terminal of the host, it works. So something between paramiko and this command seems to be the blocker.
Here is my sample script t hat should work, but seems to fail at running the command.
#!/usr/bin/env python
import boto3
import time
import subprocess
import paramiko
import StringIO
c = paramiko.SSHClient()
c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
c.connect(hostname = '12.34.56.78', username = "username", key_filename='''/Users/mallachar/Downloads/testkey.pem''' )
stdin , stdout, stderr = c.exec_command('sudo sed -i -E "s/^HOSTNAME.*/HOSTNAME=testhost.company/" /etc/sysconfig/network')
print stdout.read()
print stderr.read()
c.close
Here is me printing stdout and stderr
sudo: sorry, you must have a tty to run sudo
Pretty simple, I had to add this to the command.
get_pty=True
so
stdin , stdout, stderr = c.exec_command('sudo sed -i -E "s/^HOSTNAME.*/HOSTNAME=testhost.company/" /etc/sysconfig/network',get_pty=True)
Trying to monitor the available physical disc space of a remote machine using a python script, which executes the df -h . command using subprocess.popen.
import subprocess
import time
command = 'ssh remoteserver "df -h ."'
while True:
proc = subprocess.Popen(command,shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
output,err=proc.communicate()
print output
print err
time.sleep(60)
The script runs fine and prints the output to the terminal when run from command line
$> python2.7 script.py
Filesystem Size Used Avail Use% Mounted on
remoteserver:/home/user
555G 447G 109G 81% /home
The scripts does not produce any output or seems to be blocking when the script is started with nohup command.
$> nohup python2.7 script.py &
Would like the script to work and fetch the disc space of remote machine using the above script when started in nohup.
I'm not 100% sure of the underlying issue here, but when you invoke NOHUP in the shell, it's disconnected some of the STDIN/STDOUT from the terminal process, which I suspect it causing some of this interactions you're seeing.
Given that you're doing this from a remote machine, I'd actually recommend you look at using something like Fabric as a library to do what you're after. It's pretty straightforward, and does most of the handling of terminal sessions as well as closing things down nicely for you when you're complete.
something like:
from fabric import api
from fabric.api import env
import fabric
env.host_string = '%s#%s' % (username, remote_host)
env.disable_known_hosts = True
env.password = password
fabric.state.output['stdout'] = False
fabric.state.output['stderr'] = False
results = api.run('df -h')
You might try sending stdin=subprocess.PIPE to the subprocess command, then calling proc.stdin.close() on the next line, before the communicate() call. Or you can try changing the command to 'ssh remoteserver "df -h ." </dev/null'. Others report using FNULL = open(os.devnull, 'r') and passing in FNULL to the stdin= argument, but I'm not sure if you need to call FNULL.close() after or not.
SSH is most likely waiting for input for some reason when it is run from nohup. Perhaps it is unable to authenticate in the nohup environment and is asking for password input?
To make sure SSH is not waiting for input, try adding -o "BatchMode yes" to the ssh command and see if there are some clues in the output/error from the subprocess communicate call.
Any help on this issue would be greatly appreciated.
Basically I'm writing a python script that will ssh onto various servers and to execute scripts. The problem is that these scripts use an env variable to start. Ie the script is test.sh but we use an env variable to launch it, run test.sh.
So far the routes I have taken, such as Paramiko module execute commands but do not actually take on the env variables.
import paramiko
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect('testserver' )
stdin, stdout, stderr = ssh.exec_command(cd /home/test/;$run ./test.sh')
print stdout.readlines()
print stderr.readlines()
ssh.close()
Is there a way to use paramiko? Or another way I should take?
Thanks
# Rob
I edited the script to test. The $run variable is coming back blank.
stdin, stdout, stderr = ssh.exec_command('whoami;echo hello; echo $run ; echo goodbye')
['testuser\n', 'hello\n', '\n', 'goodbye\n']
[]
# Rob part 2
Logging onto the server, I am able to echo $run and it returns the correct path/script
I also checked and this is an env variable that is set in the .profile. I feel like python is not invoking the .profile .
ssh.exec_command doesn't interpret the remote .profile. Try this:
ssh.exec_command('. .profile ; cd /home/test/;$run ./test.sh')
Plumbum to the rescue.
Among other things, it provides a nifty interface, leveraging paramiko (see ParamikoMachine), and also lets you manipulate the remote environment.