Python, run terminal, and execute command in it - python

I am using Python 2.7.5, since this version is installed on the machine which I want to run script.
I have created a simple GUI in Tkinter, with button and text input.
Now in one input I provide the ip, or hostname of server, in next step I read the value of input fields and send it to linux bash terminal, and here I have a problem.
Reading the value from input field(works good)
nazwa_ip = self.Input_IP_hostname.get("1.0", 'end-1c')
and next:
os.system('gnome-terminal --window-with-profile=MY_PROFILE -e "ssh -t user_name#nazwa_ip"')
and here is the problem, because it wont change "nazwa_ip" to the read value. That comand send to terminal:
ssh -t user_name#nazwa_ip
but i want to send:
ssh -t user_name#ip_adres_from_input_field
Can somebody help me to resolve the issue?

according to the Python docs, it is recommended that os.system be replaced with the subprocess module .
status = os.system("mycmd" + " myarg")
# becomes
status = subprocess.call("mycmd" + " myarg", shell=True)

String formatting will work here:
os.system('gnome-terminal --window-with-profile=MY_PROFILE -e "ssh -t user_name#%s"' % nazwa_ip)

Using the subprocess method might be better to do this.
import subprocess
nazwa_ip = self.Input_IP_hostname.get("1.0", 'end-1c')
ssh_param = "ssh -t user_name#{}".format(nazwa_ip)
subprocess.call(['gnome-terminal', '--window-with-profile=MY_PROFILE', '-e', ssh_param])

Whilst running a subprocess is easy, starting one in a graphical terminal that behaves exactly like one the user launched is a little tricker. You could use my program interminal (link), which basically does what Stephen Rauch's answer does with gnome-terminal, but via a shell so that user environment variables and aliases etc are all available, which could be useful on the offchance that they affect how ssh runs.
You would use it from Python like this:
import subprocess
subprocess.Popen(['interminal', 'ssh', '-t', 'username#{}'.format(ip_address)])

Related

Using WSL bash from within python

I'm using windows 10 and while working on a new project, I need to interact with WSL(Ubuntu on windows) bash from within python (windows python interpreter).
I tried using subprocess python library to execute commands.. what I did looks like this:
import subprocess
print(subprocess.check_call(['cmd','ubuntu1804', 'BashCmdHere(eg: ls)']))#not working
print(subprocess.check_output("ubuntu1804", shell=True).decode())#also not working
The expected behavior is to execute ubuntu1804 command which starts a wsl linux bash on which I want to execute my 'BashCmdHere' and retrieve its results to python but it just freezes. What am I doing wrong ? or how to do this ?
Thank you so much
Found 2 ways to achieve this:
A correct version of my code looks like this
#e.g: To execute "ls -l"
import subprocess
print(subprocess.check_call(['wsl', 'ls','-l','MaybeOtherParamHere']))
I should have used wsl to invoke linux shell from windows aka bash then my command and parameters in separated arguments for the subprocess command.
The other way which I think is cleaner but may be heavier is using PowerShell Scripts:
#script.ps1
param([String]$folderpath, [String]$otherparam)
Write-Output $folderpath
Write-Output $otherparam
wsl ls -l $folderpath $otherparam
Then to execute it in python and get the results:
import subprocess
def callps1():
powerShellPath = r'C:\WINDOWS\system32\WindowsPowerShell\v1.0\powershell.exe'
powerShellCmd = "./script.ps1"
#call script with argument '/mnt/c/Users/aaa/'
p = subprocess.Popen([powerShellPath, '-ExecutionPolicy', 'Unrestricted', powerShellCmd, '/mnt/c/Users/aaa/', 'SecondArgValue']
, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
output, error = p.communicate()
rc = p.returncode
print("Return code given to Python script is: " + str(rc))
print("\n\nstdout:\n\n" + str(output))
print("\n\nstderr: " + str(error))
# Test
callps1()
Thank you for helping out
What about:
print(subprocess.check_call(['ubuntu1804', 'run', 'BashCmdHere(eg: ls)'])) #also try without "run" or change ubuntu1804 to wsl
Or
print(subprocess.check_call(['cmd', '/c', 'ubuntu1804', 'run', 'BashCmdHere(eg: ls)']))#also try without "run" or change "ubuntu1804" to "wsl"
# I think you need to play with quotes here to produce: cmd /c 'ubuntu1804 run BashCmdHere(eg: ls)'
First, try to call your command from cmd.exe to see the right format and then translate it to Python.
os.system('bash')
I figured this out by accident.

Execute multiple shell/command prompt commands in same instance

I am looking for a way to execute multiple commands in the same shell instance using a separate function for each, something that I can define when the shell process opens/closes and can pass commands to.
so far all the answers I have found have only been in a single function
ie:
#!/usr/bin/env python
from subprocess import check_call
check_call(r"""set -e
ls -l
<some command> # This will change the present working directory
launchMyApp""", shell=True)
I need the same effect but with each command in a different function like
shell.open()
shell.exec("dir")
shell.exec("cd C:/Users/" + User + "/Desktop)
shell.close()
if you are wondering whyyy it has to be separate the command to run is coming from user input. yes I realize that is a security risk, but security isn't a problem in this case, as its purely an educational venture and not going to be used for anything
you could use subprocess.check_call(cmds_str, shell=True) in conjunction with multiple commands in the same line: How to run two commands in one line in Windows CMD?
You could build each command individually and add them to a list, and then use ' & '.join(cmd_list) to get cmds_str.
I don't use Windows but it works on Linux.
You can try pexpect with cmd.exe
import pexpect
child = pexpect.spawn("cmd.exe")
child.expect_exact("> ")
#print(child.before.decode('utf-8'))
print(child.before)
child.sendline("dir")
child.expect_exact("> ")
print(child.before)
child.sendline("cd C:/Users/" + User + "/Desktop")
child.expect_exact("> ")
print(child.before)
It runs cmd.exe, sends command in child.sendline() and looks for prompt child.expect_exact("> ") to get all text generated by command child.before.

Is there a way to execute arp -a cmd command in python script?

I want to get mac address of all the device connected to network. [in a script - all windows environment]
I decided to use python for that. I have used nmap for it.
import nmap
nm = nmap.PortScanner()
nm.scan('127.0.0.1', '22-443')
#nm.scan(hosts='192.168.1.0/24', arguments='-n -sP -PE -PA21,23,80,3389')
nm.scan(hosts = '192.168.1.0/24', arguments = '-n -sP -PE -T5')
for host in nm.all_hosts():
mac = nm[host]['addresses']['mac']
print("mac " + mac)
[The problem with this method is that, sometime it misses few devices. Meaning,
If there are 5 devices connected to router, and I run the script first time, it will return only one or two devices. Next time I run the script it will return all 5, third time may be only one...like that.]
Where as if I open command prompt and do,
arp -a
It works perfectly every time.
So my question is that, is there a way I could parse the result from cmd in python script?
I looked onto os.popen(..??..),
But I am not able to understand exactly how can I do that?
Is there any good library for this?https://pypi.python.org/pypi/arprequest/0.3
You should have a look at the subprocess module. You probably want to use the check_output method:
import subprocess
output = subprocess.check_output(("arp", "-a"))
# Parse output here
check_output will return a str object in python 2, and a bytes object in python 3 which you can convert with output.decode("ascii") for example.

python does not execute shell command "mail" properly

I use the following bash code to send email:
echo abc | mail -s Subject abc#def.com
It works fine. Now I want to use the same command in python, so I write the following:
call(["echo abc" "|" "mail", "-s Subject", "abc#def.com"], shell=True)
This python code does not send me the email and when I call the python script using
$python email.py
I get the following information from shell
No mail for rex
How does this happen?
There's no need for echo and piping between commands if you're using Python.
You can start a process and use the communicate method:
import subprocess
def send_message(recipient, subject, body):
process = subprocess.Popen(['mail', '-s', subject, recipient],
stdin=subprocess.PIPE)
process.communicate(body)
Using subprocess.communicate as suggested elsewhere is a much better approach. You should avoid using shell=True for the reasons described here. But to answer your question why your call doesn't work, the problem is that you've broken the shell string up into rather arbitrary strings. If you pass the whole command as one string, it should work on Unix-y platforms at least.
call(["echo abc|mail -s Subject abc#def.com"], shell=True)
See the details here. But such usage is not recommended.

pexpect ssh not able to handle command options

am using pexpect ssh to write down a script for compilation, the ssh automation looks like this,
enter code here
child = ssh_expect('root', server2, cmd)
child.expect(pexpect.EOF)
print child.before
where cmd is this:
cmd = "./configure CFLAGS=\"-g -O0 -DDEBUG\""
the problem happens is that it says,
configure: error: unrecognized option: -O0
whereas, if run the same command using commands.getoutput then it executes properly.
Question what is the problem that this kind of error is getting generated and how can I erradicate this one?
thanks in advance :)
The reason it's working if you're doing commands.getoutput is that all commands there are run though a shell, which will parse your commandline and understand that what's between the double quotes after CFLAGS is part of the same parameter.
When you're running cmds through pexpect, no shell is involved. Also, there's no shell involved on the other side of the ssh connection when you provide commands on the ssh commandline, so there's nothing that parse CFLAGS into one parameter. Therefore, instead of the configure script getting one parameter (CFLAGS=\"-g -O0 -DDEBUG\"), it gets three parameters ('CFLAGS=-g', '-O0', '-DDEBUG').
If possible, avoid sending commands where parameters are separated by spaces. It seems pexpect can take a list of arguments instead. Working code example:
#/usr/bin/env python
import pexpect
def ssh_expect(user, hostname, cmd):
child = pexpect.spawn("ssh", ["%s#%s" % (user, hostname)] + cmd, timeout=3600)
return child
child = ssh_expect("root", "server.example.com", ["./configure", "CFLAGS=\"-g -O0 -DDEBUG\""])
child.expect(pexpect.EOF)
print child.before

Categories