pexpect ssh not able to handle command options - python

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

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.

Using Osgeo4w shell via. python script

I'm trying to write a script that creates multiple ogr2ogr calls to a WFS service (in a loop). For some reason i can't use the osgeo lib (it's a work computer, with limited access..), so i figured i would give the Subprocess lib a try.
My though process so far is:
open OSGeo4W shell
transfer string from script to shell command line
loop for multiple ogr2ogr calls
Code:
import subprocess
p = subprocess.Popen(r'C:\Program Files\QGIS 2.18\OSGeo4W.bat',
stdout=subprocess.PIPE, stdin=subprocess.PIPE)
call = 'ogr2ogr -f "CSV" "folder_on_pc" WFS:"dbname" -sql "SELECT * from
specific_layer where attribute>=20180311 ORDER BY attribute"'
subprocess.check_call(call, shell=True)
output = p.communicate(call)[0]
I know the ogr2ogr call works, but can't seem to make the command line 'type it'. If this is a completly wrong approach, please tell me. I appriciate all help.

Python, run terminal, and execute command in it

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)])

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.

How can I pass file names to external commands executed from Python?

I am trying to execute a command inside a Python script:
import subprocess
output_process =
subprocess.Popen("javac -cp C:\Users\MyUsername\Desktop\htmlcleaner-2.2.jar Scrapping_lastfm.java",
shell=True, stdout=subprocess.PIPE)
But I am getting an error package org.htmlcleaner does not exist.
If I run the javac command independently, it executes fine..
My current working directry is C:\Users\MyUsername.
The error is not raised by python but by the java subprocess. Most likely the java machine is not finding some libraries, and that refines the problem to a PATH configuration problem, most likely
the variable CLASSPATH has not been set in the environment. to solve :
import shlex
JAVA_COMMAND=r"javac -cp C:\\Users\\MyUsername\\Desktop\\htmlcleaner-2.2.jar Scrapping_lastfm.java"
cmdline = shlex.split(JAVA_COMMAND)
output_process = subprocess.Popen(cmdline,shell=True, stdout=subprocess.PIPE, env={'CLASSPATH':'/path/to/java/packages'})
Try
output_process = subprocess.Popen(["javac", "-cp",
"C:\Users\MyUsername\Desktop\htmlcleaner-2.2.jar", "Scrapping_lastfm.java"],
shell=True, stdout=subprocess.PIPE, env={'ENVIRONMENTAL': '/variables/here'})
with whatever java-related environmental variables you have when you run javac normally as items in the env dictionary. asgs suggests you need CLASSPATH.
You don't have to split the command up into a list I just did that to make it easier to see the whole thing.
Be aware, that you have to escape the backslash (\) in the string. Your example is fine, however if your username is not actually MyUsername but maybe „nerd“ or any other string forming a valid escape-sequence, the command will fail.
Also make sure that you don't have spaces in the filename (or use the split syntax in the other example).
So you might want to do:
output_process = subprocess.Popen(["javac", "-cp",
"C:\\Users\\MyUsername\\Desktop\\htmlcleaner-2.2.jar", "Scrapping_lastfm.java"],
shell=True, stdout=subprocess.PIPE)

Categories