Python3 Tkinter - Write input (y) to console for subprocess - python

I've been looking around for an answer to my problem but have been unlucky. I would like for the answer to work with native python and hopefully be simple.
My problem is that I'm using subprocess in my tkinter application, but one of the commands require you to write Y/N to be sure you want to proceed with the action.
So I'm looking for a way to write y into the terminal when a message like this appears:
Are you sure you want to continue? (y/N)
I've tried by running subprocess.run("y") but that doesn't seem to work.
I'm testing this on Debian Linux and to call the command that asks if I want to proceed, is subprocess.getoutput() so that I can check for errors.
CODE
class RemovePublicKeyDialog:
def __init__(self, parent):
top = self.top = Toplevel(parent)
Label(top, text="Who to remove?").pack()
self.e = Entry(top)
self.e.pack(padx=5)
b = Button(top, text="REMOVE", command=self.ok)
b.pack(pady=5)
def ok(self):
#print("value is " + self.e.get())
key = self.e.get()
cmd = subprocess.getoutput("gpg --delete-keys " + key)
print(cmd)
if ("key \"" + key + "\" not found" in cmd):
messagebox.showerror("Error", "No such public key.")
elif ("Delete this key from keyring?" in cmd):
#subprocess.run("echo 'y'")
messagebox.showinfo("Success", "Public key \"" + key + "\" deleted from keyring.")
else:
messagebox.showerror("Error", "Unknown error, did you input a key?")
self.top.destroy()
This is the "main" code, everything works but it's just that I need to input Y to get it to proceed.

Many command line utilities have a flag that automatically answers yes to any prompts - if you have access to the source code of your particular command, adding such a flag if it doesn't have one (or simply making a custom version that never prompts) may be the easiest solution. Some commands automatically do this if not run directly from a terminal - are you sure that this is even a problem?
If you know that there will be a single prompt, you could try:
subprocess.run("echo y | your-command", shell=True)
If there may be multiple prompts, you'd have to use one of the more complex options in the subprocess module, reading and parsing the command output to know when a reply is needed.

Related

Why am I getting an "No maching processes were found" error upon executing this python file?

I am working on a project in Python. Currently, only Option 1 works. (i just started this project) It goes through two levels of input and then runs a shell command that makes MacOS show a hidden file. But I am getting an error in the Macos Terminal upon running it: "No matching processes were found." Do you have any idea on why it could be doing this?
My code is too large to fit here, so here is a pastebin link: https://pastebin.com/EuBJjge6
#!/usr/bin/env python
import os
#function by popcnt on stackoverflow
def clear():
os.system('cls' if os.name =='nt' else 'clear')
# thanks to No Spoko on StackOverflow for the original idea, what you see here is modified
def printLogo():
f = open('logo.txt', 'r')
logo = f.read()
print(logo)
f.close()
clear()
printLogo()
print('Welcome to Belowdeck! \n')
#some code ideas from stackoverflow, don't remember the user
menuItem = {}
menuItem['1']="Show hidden files and folders"
menuItem['2']="Hide a file or folder from view"
menuItem['3']="Download files without a web browser"
menuItem['4']="List contents of a folder"
menuItem['5']="View any file's contents"
menuItem['6']="Change permissions of a file"
menuItem['7']="Restore a disk image to a device connected to your Mac"
menuItem['8']="Change the default screenshot location"
menuItem['9']="Change the default screenshot format"
menuItem['10']="Stop apps from syncing to iCloud by default"
menuItem['11']="Check for macOS updates every day instead of every month"
menuItem['12']="Enable a sound when your Mac connects to a power source"
menuItem['13']="Make holding down a key repeat characters"
menuItem['14']="Dull hidden apps in your Dock"
menuItem['15']="Hide non-active apps in your Dock"
menuItem['16']="Add a spacer to your Dock"
menuItem['17']="Change the delay before your Dock slides out"
menuItem['18']="Change the speed at which your Dock slides out"
menuItem['19']="Disable auto-restore in the Preview app"
menuItem['20']="Add a message to the login window"
menuItem['21']="Get rid of Dashboard"
menuItem['22']="Rebuild Spotlight"
menuItem['23']="Destroy your Mac \n"
options=menuItem.keys()
sorted(options)
for entry in options:
print (entry, menuItem[entry])
def option1():
clear()
print("Show hidden files and folders \n")
selection1=input("Would you like to proceed with the operation? (y/n): ")
if selection1 == "y" or selection1 == "Y":
os.system("defaults write com.apple.finder AppleShowAllFiles -bool TRUE && killall finder")
else:
input('Invalid selection! ')
while True:
menuSelection=input("Please select your desired action:")
if menuSelection =='1':
option1()
elif menuSelection == '2':
print ("delete")
elif menuSelection == '3':
print ("find")
elif menuSelection == '4':
print ('4')
else:
print ("Invalid action!")
I don't have access to a Mac, but Googling that error message throws up a lot of hits where people have had issues trying to kill processes. It's very likely that the issue is in line 59. Make sure the commands are correct. Also, avoid using os.system() call to execute shell commands -- it is not recommended. Use the subprocess module instead.
https://docs.python.org/3/library/subprocess.html#subprocess-replacements
https://www.youtube.com/watch?v=oQxTSDh-ECk

using subprocess.run to automate a command line application (windows 10)

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

This python code that gets a pass word is not working?

This python code is supposed to ask a user for their password then print out the pass word but it keeps saying ho! It worked for a little while but then I lost the edited code.
import sys, os, re, subprocess
def askpass(prompt):
prompt = prompt.replace('"', "'")
if 'yes/no' in prompt:
return "yes"
script="""
tell application "Finder"
activate
display dialog "%s" \
with title "Title" \
default answer "" \
with icon caution \
with hidden answer
end tell
""" % prompt
p = subprocess.Popen(['osascript', '-e', script], stdout=subprocess.PIPE)
out = p.stdout.read()
g = re.match("text returned:(.*), button returned:.*", out)
if not g:
return "HO"
return g.group(1)
return out
print askpass('decription')
I tried here, and the returned text is switched.
You expect text returned:(.*), button returned:.*, but the AppleScript has returned in format button returned:.*, text returned:(.*).
I suggest you use:
g = re.search(r'text returned:(\S*)', out)
Or something like this.

Simulating CLI Shell with python

I was asked to simulate CLI with Python.
This is what I did
def somefunction(a,b):
//codes here
//consider some other functions too
print "--- StackOverFlow Shell ---"
while True:
user_input = raw_input("#> ")
splitit = user_input.split(" ")
if splitit[0] == "add":
firstNum = splitit[1]
sNum = splitit[2]
result = somefunction(firstNum, sNum)
print result
//consider some other elif blocks with "sub", "div", etc
else:
print "Invalid Command"
I do also check the length of the list, here "splitit" I will allow only 3 argumets, first will be the operation, and second and third are the arguments with which some functions are to be performed, in case the argument is more than 3, for that i do put a check.
Though Somehow I manage to make it work, but is there a better way to achieve the same?
Use python CMD Module:
Check few examples given on the below pages
http://docs.python.org/library/cmd.html # Support for line-oriented command interpreters
http://www.doughellmann.com/PyMOTW/cmd - # Create line-oriented command processors
prompt can be set to a string to be printed each time the user is asked for a new command.
intro is the “welcome” message printed at the start of the program.
eg:
import cmd
class HelloWorld(cmd.Cmd):
"""Simple command processor example."""
prompt = 'prompt: '
intro = "Simple command processor example."
You should check out the VTE lib:
http://earobinson.wordpress.com/2007/09/10/python-vteterminal-example/
It works really well and you can very easily customize its look. This is how easy it is:
# make terminal
terminal = vte.Terminal()
terminal.connect ("child-exited", lambda term: gtk.main_quit())
terminal.fork_command()
# put the terminal in a scrollable window
terminal_window = gtk.ScrolledWindow()
terminal_window.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
terminal_window.add(terminal)

Python / Pexpect before output out of sync

I'm using Python/Pexpect to spawn an SSH session to multiple routers. The code will work for one router but then the output of session.before will get out of sync with some routers so that it will return the output from a previous sendline. This seems particularly the case when sending a blank line (sendline()). Anyone got any ideas? Any insight would be really appreciated.
Below is a sample of what I'm seeing:
ssh_session.sendline('sh version')
while (iresult==2):
iresult = ssh_session.expect(['>','#','--More--'],timeout=SESSION_TIMEOUT)
debug_print("execute_1 " + str(iresult))
debug_print("execute_bef " + ssh_session.before)
debug_print("execute_af " + ssh_session.after)
thisoutput = ssh_session.before
output += thisoutput
if(iresult==2):
debug_print("exec MORE")
ssh_session.send(" ")
else:
debug_print("exec: end loop")
for cmd in config_commands:
debug_print("------------------------------------------------\n")
debug_print ("running command " + cmd.strip() + "\n")
iresult=2
ssh_session.sendline(cmd.strip())
while (iresult==2):
iresult = ssh_session.expect([prompt+">",prompt+"#"," --More-- "],timeout=SESSION_TIMEOUT)
thisoutput = ssh_session.before
debug_print("execute_1 " + str(iresult))
debug_print("execute_af " + ssh_session.after)
debug_print("execute_bef " + thisoutput)
thisoutput = ssh_session.before
output += thisoutput
if(iresult==2):
debug_print("exec MORE")
ssh_session.send(" ")
else:
debug_print("exec: end loop")
I get this:
logged in
exec: sh version
execute_1 1
execute_bef
R9
execute_af #
exec: end loop
------------------------------------------------
running command config t
execute_1 1
execute_af #
execute_bef sh version
Cisco IOS Software, 1841 Software (C1841-IPBASEK9-M), Version 15.1(4)M4, RELEASE SOFTWARE (fc1)
Technical Support: http://www.cisco.com/techsupport...
I've run into this before with pexpect (and I'm trying to remember how I worked around it).
You can re-synchronize with the terminal session by sending a return and then expecting for the prompt in a loop. When the expect times out then you know that you are synchronized.
The root cause is probably that you are either:
Calling send without a match expect (because you don't care about the output)
Running a command that produces output but expecting for a pattern in the middle of that output and then not to next prompt that is at end of the output. One way to deal with this is to change your expect pattern to "(.+)PROMPT" - this will expect until the next prompt and capture all the output of the command sent (which you can parse in the next step).
I faced a similar problem. I tried waiting for the command to be printed on the screen and the sending enter.
I you want to execute say command 'cmd', then you do:
session.send(cmd)
index = session.expect([cmd, pexpect.TIMEOUT], 1)
session.send('\n')
index = session.expect([whatever you expect])
Worked for me.
I'm not sure this is the root of your problem, but it may be worth a try.
Something I've run into is that when you spawn a session that starts with or lands you in a shell, you have to deal with quirks of the TERM type (vt220, color-xterm, etc.). You will see characters used to move the cursor or change colors. The problem is almost guaranteed to show up with the prompt; the string you are looking for to identify the prompt appears twice because of how color changes are handled (the prompt is sent, then codes to backspace, change the color, then the prompt is sent again... but expect sees both instances of the prompt).
Here's something that handles this, guaranteed to be ugly, hacky, not very Pythonic, and functional:
import pexpect
# wait_for_prompt: handle terminal prompt craziness
# returns either the pexpect.before contents that occurred before the
# first sighting of the prompt, or returns False if we had a timeout
#
def wait_for_prompt(session, wait_for_this, wait_timeout=30):
status = session.expect([wait_for_this, pexpect.TIMEOUT, pexpect.EOF], timeout=wait_timeout)
if status != 0:
print 'ERROR : timeout waiting for "' + wait_for_this + '"'
return False
before = session.before # this is what we will want to return
# now look for and handle any additional sightings of the prompt
while True:
try:
session.expect(wait_for_this, timeout=0.1)
except:
# we expect a timeout here. All is normal. Move along, Citizen.
break # get out of the while loop
return before
s = pexpect.spawn('ssh me#myserver.local')
s.expect('password') # yes, we assume that the SSH key is already there
# and that we will successfully connect. I'm bad.
s.sendline('mypasswordisverysecure') # Also assuming the right password
prompt = 'me$'
wait_for_prompt(s, prompt)
s.sendline('df -h') # how full are my disks?
results = wait_for_prompt(s, prompt)
if results:
print results
sys.exit(0)
else:
print 'Misery. You lose.'
sys.exit(1)
I know this is an old thread, but I didn't find much about this online and I just got through making my own quick-and-dirty workaround for this. I'm also using pexpect to run through a list of network devices and record statistics and so forth, and my pexpect.spawn.before will also get out of sync sometimes. This happens very often on the faster, more modern devices for some reason.
My solution was to write an empty carriage return between each command, and check the len() of the .before variable. If it's too small, it means it only captured the prompt, which means it must be at least one command behind the actual ssh session. If that's the case, the program sends another empty line to move the actual data that I want into the .before variable:
def new_line(this, iteration):
if iteration > 4:
return data
else:
iteration+=1
this.expect(":")
this.sendline(" \r")
data = this.before
if len(data) < 50:
# The numer 50 was chosen because it should be longer than just the hostname and prompt of the device, but shorter than any actual output
data = new_line(this, iteration)
return data
def login(hostname):
this = pexpect.spawn("ssh %s" % hostname)
stop = this.expect([pexpect.TIMEOUT,pexpect.EOF,":"], timeout=20)
if stop == 2:
try:
this.sendline("\r")
this.expect(":")
this.sendline("show version\r")
version = new_line(this,0)
this.expect(":")
this.sendline("quit\r")
return version
except:
print 'failed to execute commands'
this.kill(0)
else:
print 'failed to login'
this.kill(0)
I accomplish this by a recursive command that will call itself until the .before variable finally captures the command's output, or until it calls itself 5 times, in which case it simply gives up.

Categories

Resources