I am trying to write a CLI program with python in windows. I recently came across an issue with argparse and my work-around was to clear the command prompt and re-run my script with the same arguments using this code:
sys.stdout.flush
os.execl(sys.executable, 'python', __file__, *sys.argv[1:])
This works perfectly until I try to input something into my program. I have this perpetual loop in my code which allows me to input a string and have it echoed back to me:
while True:
action = input("> ")
print(f'typed {action}')
but once I reach that point in the code, it does not work, this is my output:
This is the expected output up until after the red "Ready."
for some reason, the command prompt alternates between an input to my script, and an input to the command prompt, which are both prefaced with the wrong text, the ">" should be an input to my script, but that input is being sent to the command prompt, and the "...\raspi>" should be an input to the command prompt, but that input is being sent to my script. Anyone have any ideas as to why this is happening? Why it's alternating between my script and regular command prompt?
The solution I came to was to realize that the cprint() function of the colorama.py library does not return text and instead just calls the print function (I should have been able to deduce that through the name of the function). So all I had to do was to not set the description of my parser to a cprint() function and instead call cprint() when it's needed.
Related
Edited* Solution: Remove "pause".
I'm running a python script which calls upon powershell to execute a line of code:
def download():
subprocess.call('C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe yt-dlp https://www.youtube.com/watch?v=jtjnnykvnh4;pause', shell=True)
download()
The problem was that after executing, it would output "Press Enter to continue..." This interrupts the program.*in my original example I forgot to include the ";pause" which is what turned out to be what was causing the interruption in the program, as kindly pointed out by the marked answer.
Below is the fixed line of code which does not prompt "press enter to continue" after running:
def download():
subprocess.call('C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe yt-dlp https://www.youtube.com/watch?v=jtjnnykvnh4;kill $pid', shell=True)
download()
Apologies for confusion caused by the original post. Thanks for the help.
PowerShell normally exits unless you specify -NoExit at the commandline. Even then it will not include the message you are seeing unless you add a pause at the end instead. Even so, I would expect your command to look more like
'C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe & {yt-dlp https://www.youtube.com/watch?v=jtjnnykvnh4}'
My guess this has more to do with Python, though I have not encountered it before...have you tried executing the PowerShell line from another commandline (cmd on Windows or bash on Linux/Mac or another favourite) to verify that you get the same result independently of Python?
Another possibility is that it is the yt-dlp tool that you are using that has the pause effect (I am not familiar with the tool). Is it a PowerShell module? Or is it something that can be run on the commandline and you don't need PowerShell as a middleman anyway? Would it have a "silent" or "-q" argument, or another more relevant argument?
DAOPHOT is a FORTRAN-written software for performing astronomy tasks in images. A typical flow of its usage is:
Open a terminal (gnome-terminal in my case) and run ./daophot. I'm now within DAOPHOT's shell.
Prompts the user for a command, let's say ATTACH to input an image file. DAOPHOT runs and prompts the user again for more commands.
User gives another command, let's say PHOTOMETRY. DAOPHOT runs and prompts the user again.
For every command the user gives, DAOPHOT runs and prompts again and again until exit is typed. For my case, I have three specific commands that will run one after another, without variation (ATTACH, PHOTOMETRY and PSF, with the latter maybe run more than once).
Right now I'm simply trying to ATTACH a file. What I have tried:
Using subprocess, as seen/asked here and here:
import subprocess
p = subprocess.Popen(["gnome-terminal","--disable-factory","--","./daophot"], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
p.stdin.write(input("ATTACH file.fits"))
For this case, DAOPHOT's shell opens but the ATTACH command is not executed. I close the shell and the string "ATTACH file.fits" appears in the IPython terminal, ending the subprocess. I've tried also to use p.communicate(input=input("ATTACH file.fits")), but got the same result.
Using pexpect, as seen/asked here and here:
import pexpect
p = pexpect.spawn("gnome-terminal --disable factory -- ./daophot")
p.expect(pexpect.EOF)
p.sendline("ATTACH file.fits")
In this case, DAOPHOT's shell opens but the ATTACH command is not accounted for as an input.
Finally, a DAOPHOT wrapper already exists, but the idea is to have this automatically and interactive Python version in our lab, so that we can change later if needed.
From what I understand in terms of pipelines, ./daophot is a subsubprocess runnning inside gnome-terminal, so when I use e.g. p.stdin.write(input("ATTACH file.fits") I am actually inputing this command into gnome-terminal, and not into ./daophot.
Any help is much appreciated.
This is a weird one that's so general I can't to properly narrow the search terms to find an answer.
My python script has a raw_input to prompt the user for values. But, when I try to run the script and funnel it into a file, it crashes.
Something like "script.py > save.txt"
wont work. It doesn't even properly prompt me at the command line for my input. There doesn't seem to be anything indicating why this doesn't work as intuitively as it should.
raw_output prints its prompt to stdout, which you are redirecting to a file. So your prompt will end up in the file and the program does not appear to show a prompt. One solution is to output your prompt to stderr.
import sys
sys.stderr.write('prompt> ')
value = raw_input()
print('value was: ', value)
You could also avoid using both pipes and interactive input with the same script. Either take input from command line flags using argparse and use pipes, or create an interactive program that saves output to a file itself.
Depending on your program's logic, you can also check whether stdout is connected to a live console or not:
is_tty = os.isatty(sys.stdout.fileno())
Dolda2000 also has a good point about writing to /dev/tty, which will write to the controlling terminal of the script being run even if both stdin and stderr are redirected. The deal there, though, is that you can't use it if you're not running in a terminal.
import errno
try:
with open('/dev/tty', 'w') as tty:
tty.write('prompt> ')
except IOError as exc:
if exc.errno == errno.ENXIO:
pass # no /dev/tty available
else:
pass # something else went wrong
I have this python function using pexpect that looks something like this:
def executeCommandWithOtherUser(command):
p = pexpect.spawn('su', ['otherUser'])
p.expect('Password:')
p.sendline('1234')
prompt = '.*$'
p.expect(prompt)
p.sendline(command)
#p.interact()
p.close()
return
When I try this code, it doesn't execute the command (tested using e.g. touch x.txt). I can see the command if I enable pexpect logging but nothing has happened when the program finishes. If I uncomment the line with p.interact() it does work. However, this gives the control to the user and I need to manually exit the program.
Why doesn't the function execute the command without interact? What can I do to make it work?
You are inserting your command immediately after entering a password -- which is BEFORE the remote end is ready to receive your command.
You need to expect a remote prompt before sending your command.
The problem is that the prompt regex is not correctly specified (prompt = '.*$'). The current prompt string regex will match also empty inputs because $ has a special regex meaning and is not escaped with \. The below code works:
def executeCommandWithOtherUser(command):
p = pexpect.spawn('su', ['otherUser'])
p.expect('Password:')
p.sendline('1234')
prompt = r'\$'
p.expect(prompt)
p.sendline(command)
p.expect(prompt)
p.close()
return
An alternative to expect() is to use expect_exact(), which uses plain string matches instead of regex.
I am trying to issue an external shell command via python script. The external command prompts user to type 'Y' and hit enter to proceed further. How do I write the command in python script in a loop so that it does not prompts the user in every loop. I can grab a coffee and step out instead of sitting in front of the PC and hit Y every time I see the prompt.
My python script loops like
<for loop:>
os.system(<External Command>)
I tried echoing "Y\n" but did not work.
use the yes command, the following code should do what you want:
<for loop:>
os.system('yes Y | <external command>')
more info here
Check out pexpect. You can write a script to watch for your command's output, and respond appropriately.