trying to use python to automate a usage of a command line application called slsk-cli
manually, the procedure is straight-forward - i open a command prompt window and type 'soulseek login', then a prompt requests username, after i type in and press enter i'm requested a password.
so far, i manage to get the prompt of the username but not getting passed that.
subprocess.run('soulseek login',shell=True)
this results in the ?Login output in the python console but also the process is stuck, when i run in debug or also in run
is there a better way to go about this?
Interacting continuously with a system via subprocess can be tricky. However, it seems that your interface prompts are one after the other, which can therefore be chained together, via newline characters which act as Return key strokes.
For example, the program shown below simply prompts a user for their username and a password, to which the 'user' (your script) provides the input via the proc.communicate() method. Once these are provided, the user is asked if they'd like to continue (and do the same thing again). The following subprocess call feeds the following input into the prompter.py script:
username
password
continue reply (y or n)
Example code:
import subprocess
uid = 'Bob'
pwd = 'MyPa$$w0rd'
reply = 'n'
with subprocess.Popen('./prompter.py',
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
stdin=subprocess.PIPE,
text=True) as proc:
stdout, stderr = proc.communicate(input='\n'.join([uid, pwd, reply]))
Output:
# Check output.
>>> print(stdout)
Please enter a username: Password: uid='Bob'
pwd='MyPa$$w0rd'
Have another go? [y|n]:
# Sanity check for errors.
>>> print(stderr)
''
Script:
For completeness, I've included the contents of the prompter.py script below.
#!/usr/bin/env python
from time import sleep
def prompter():
while True:
uid = input('\nPlease enter a username: ')
pwd = input('Password: ')
print(f'{uid=}\n{pwd=}')
sleep(2)
x = input('Have another go? [y|n]: ')
if x.lower() == 'n':
break
if __name__ == '__main__':
prompter()
Related
I am writing a CLI that accepts an email and password for auth.
The email prompt uses raw_input() and the password prompt uses getpass() for obfuscation.
This setup works fine when outputting directly to console, but falters when redirecting the output to a log file.
Sample code:
user_email = raw_input('Email: ')
user_password = getpass('Password: ')
Sample output without redirection:
$ python script_that_does_stuff.py
Email: me#email.com
Password:
Doing stuff...
Sample output with redirection:
$ python script_that_does_stuff.py > stuff.log
Because I know that it's expecting a user input here, I can type the email, hit enter, and then it will show:
$ python script_that_does_stuff.py > stuff.log
me#email.com
Password:
After inputting a password, it continues as usual, however the log shows the following:
$ cat stuff.log
Email:Doing stuff...
Question:
How can I force the raw_input() prompt to show up in console like the getpass() prompt does when redirecting output to a file?
Environment
This script lives in a legacy Python 2.7 codebase, and is run primarily on Mac OS systems, occasionally Linux.
You can override sys.stdout temporarily to write to the terminal. For example,
import contextlib
import sys
#contextlib.contextmanager
def output_to_terminal():
try:
with open("/dev/tty") as f:
sys.stdout = f
yield
finally:
# Ensure sys.stdout is restored in the event of an error
sys.stdout = sys.__stdout__
with output_to_terminal():
x = raw_input("> ")
print(x)
(This was derived independently; you may want to check source for Python 3's redirect_stdout, also found in the contextlib module, and back port it for your use.)
This answer on another question seems to work for me.
In short, create a custom input function:
def email_input(prompt=None):
if prompt:
sys.stderr.write(str(prompt))
return raw_input()
The calling code then becomes:
user_email = email_input('Email: ')
user_password = getpass('Password: ')
This results in both the Email and Password prompts being sent to stderr (printing to console), and not messing with the redirected log output.
According to official documentation getpass([prompt[, stream]]) has the second optional parameter which indicates output stream to print the prompt to (stderr by default).
When you redirect the output (stdout) the prompt is still printed to stderr for getpass but raw_input does not support setting an output stream so its prompt is redirecting to to the target file.
So to solve your issue, you have to print your prompt to stderr for email as well.
I am trying to understand how to execute a command/program using python subprocess module & respond to the prompt by giving input.
Sample Program Program1.py which can take multiple input's:
arr1=[]
username = input("Enter your username1 : ")
password = input("Enter your password1 : ")
arr1.append((username,password))
username = input("Enter your username2 : ")
password = input("Enter your password2 : ")
arr1.append((username,password))
username = input("Enter your username3 : ")
password = input("Enter your password3 : ")
arr1.append((username,password))
username = input("Enter your username4 : ")
password = input("Enter your password4 : ")
arr1.append((username,password))
for item in arr1:
print("username:",item[0],"Password:",item[1])
I have written another program called Execute_Program1.py
import subprocess
proc = subprocess.Popen('python Program1.py',stdout=subprocess.PIPE,stderr=subprocess.PIPE,stdin=subprocess.PIPE,shell=True,universal_newlines=True)
print(proc.poll())
while proc.poll() is None:
print(proc.stdout)
proc.stdin.write('inputdata\n')
But this program is not able to execute the Program1.py. I have read many posts related to this. As per the information I have got proc.poll() return's None then the command executed by Popen is still active.
But my program is giving the following output:
None
<_io.TextIOWrapper name=4 encoding='cp1252'>
I am using Python version 3.7.4. Could anyone please help me with some inputs where I am doing mistake?
Thanks in advance.
I could not get it to work with subprocess at this time but I wanted you to have an ASAP working solution using the os module in case you need this quickly and are not absolutely required to use the subprocess module...
First add this line to the end of Program1.py, with no indentation, to prevent the command window from exiting before you have a chance to see the output:
input("Press <enter> to continue")
Next, replace your entire code in Execute_Program1.py with the following:
import os
os.system("python Program1.py 1")
This worked beautifully in Python 3.8.1 on win10pro.
Here's my sample python code
def gitClone():
proc = subprocess.Popen(['git', 'clone', 'https://someuser#sailclientqa.scm.azurewebsites.net:443/sailclientqa.git'], stdout=subprocess.PIPE)
stdout,stderr = proc.communicate('mypassword')
print(stddata)
print (stderr)
Every execution results in prompt for password. What's the best way for me to do this git clone from python without password prompt.
I'd appreciate your expertise/advice
The naive approach would be to add stdin=subprocess.PIPE to your command so you can feed it password input. But that's not that simple with password inputs which use special ways of getting keyboard input.
On the other hand, as stated in this answer it is possible to pass the password in command line (although not advised since password is stored in git command history with the command line !).
I would modify your code as follows:
add a password parameter. If not set, use python getpass module to prompt for password (maybe you don't need that)
if the password is properly set, pass it in the modified command line
my proposal:
import getpass
def gitClone(password=None):
if password is None:
password = getpass.getpass()
proc = subprocess.Popen(['git', 'clone', 'https://someuser:{}#sailclientqa.scm.azurewebsites.net:443/sailclientqa.git'.format(password)], stdout=subprocess.PIPE)
stdout,stderr = proc.communicate()
print(stddata)
print (stderr)
An alternative if the above solution doesn't work and if you cannot setup ssh keys, is to use pexpect module which handles special password input streams:
def gitClone(mypassword):
child = pexpect.spawn('git clone https://someuser#sailclientqa.scm.azurewebsites.net:443/sailclientqa.gi')
child.expect ('Password:')
child.sendline (mypassword)
It has the nice advantage of not storing the password in git command history.
source: How to redirect data to a "getpass" like password input?
import subprocess
child = subprocess.Popen(['python', 'simple.py'], stdin=subprocess.PIPE)
child.communicate('Alice')
I know you can communicate with executed script via communicate
How do you check for whether a script 'simple.py' is asking for user input?
simple.py could ask for 5-10 user inputs so simply hardcoding communicate wouldnt be enough.
[EDIT]: want to parse the stdout as the script is running and communicate back to the script
while True:
if child.get_stdout() == '?':
# send user input
A simple example:
simple.py:
i = raw_input("what is your name\n")
print(i)
j = raw_input("What is your age\n")
print(j)
Read and write:
import subprocess
child = subprocess.Popen(['python2', 'simple.py'], stdin=subprocess.PIPE, stdout=subprocess.PIPE)
for line in iter(child.stdout.readline, ""):
print(line)
if "name" in line:
child.stdin.write("foo\n")
elif "age" in line:
child.stdin.write("100\n")
Output:
what is your name
foo
What is your age
100
I have a script that I want to run from within Python (2.6.5) that follows the logic below:
Prompts the user for a password. It looks like ("Enter password: ") (*Note: Input does not echo to screen)
Output irrelevant information
Prompt the user for a response ("Blah Blah filename.txt blah blah (Y/N)?: ")
The last prompt line contains text which I need to parse (filename.txt). The response provided doesn't matter (the program could actually exit here without providing one, as long as I can parse the line).
My requirements are somewhat similar to Wrapping an interactive command line application in a Python script, but the responses there seem a bit confusing, and mine still hangs even when the OP mentions that it doesn't for him.
Through looking around, I've come to the conclusion that subprocess is the best way of doing this, but I'm having a few issues. Here is my Popen line:
p = subprocess.Popen("cmd", shell=True, stdout=subprocess.PIPE,
stderr=subprocess.STDOUT, stdin=subprocess.PIPE)
When I call a read() or readline() on stdout, the prompt is printer to the screen and it hangs.
If I call a write("password\n") for stdin, the prompt is written to the screen and it hangs. The text in write() is not written (I don't the cursor move the a new line).
If I call p.communicate("password\n"), same behavior as write()
I was looking for a few ideas here on the best way to input to stdin and possibly how to parse the last line in the output if your feeling generous, though I could probably figure that out eventually.
If you are communicating with a program that subprocess spawns, you should check out A non-blocking read on a subprocess.PIPE in Python. I had a similar problem with my application and found using queues to be the best way to do ongoing communication with a subprocess.
As for getting values from the user, you can always use the raw_input() builtin to get responses, and for passwords, try using the getpass module to get non-echoing passwords from your user. You can then parse those responses and write them to your subprocess' stdin.
I ended up doing something akin to the following:
import sys
import subprocess
from threading import Thread
try:
from Queue import Queue, Empty
except ImportError:
from queue import Queue, Empty # Python 3.x
def enqueue_output(out, queue):
for line in iter(out.readline, b''):
queue.put(line)
out.close()
def getOutput(outQueue):
outStr = ''
try:
while True: # Adds output from the Queue until it is empty
outStr+=outQueue.get_nowait()
except Empty:
return outStr
p = subprocess.Popen("cmd", stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=False, universal_newlines=True)
outQueue = Queue()
errQueue = Queue()
outThread = Thread(target=enqueue_output, args=(p.stdout, outQueue))
errThread = Thread(target=enqueue_output, args=(p.stderr, errQueue))
outThread.daemon = True
errThread.daemon = True
outThread.start()
errThread.start()
try:
someInput = raw_input("Input: ")
except NameError:
someInput = input("Input: ")
p.stdin.write(someInput)
errors = getOutput(errQueue)
output = getOutput(outQueue)
Once you have the queues made and the threads started, you can loop through getting input from the user, getting errors and output from the process, and processing and displaying them to the user.
Using threading it might be slightly overkill for simple tasks.
Instead os.spawnvpe can be used. It will spawn script shell as a process. You will be able to communicate interactively with the script.
In this example I passed password as an argument, obviously that is not a good idea.
import os
import sys
from getpass import unix_getpass
def cmd(cmd):
cmd = cmd.split()
code = os.spawnvpe(os.P_WAIT, cmd[0], cmd, os.environ)
if code == 127:
sys.stderr.write('{0}: command not found\n'.format(cmd[0]))
return code
password = unix_getpass('Password: ')
cmd_run = './run.sh --password {0}'.format(password)
cmd(cmd_run)
pattern = raw_input('Pattern: ')
lines = []
with open('filename.txt', 'r') as fd:
for line in fd:
if pattern in line:
lines.append(line)
# manipulate lines
If you just want a user to enter a password without it being echoed to the screen just use the standard library's getpass module:
import getpass
print("You entered:", getpass.getpass())
NOTE:The prompt for this function defaults to "Password: " also this will only work on command lines where echoing can be controlled. So if it doesn't work try running it from terminal.