Programmatically injecting command into Python Cmd's cmdloop - python

Using Python Cmd2 module (inherits from Cmd) to create a MyCmd CLI for my program. In most cases, I am just using my script normally with the built-in commands. The ‘quit’ command is implemented to do some cleanup for the script and subsequently exit. The Cmd2 module waits for input via the raw_input.
In a specific case, I want to programmatically invoke ‘quit’ instead of issuing it at the command line. This is done via a separate thread where a REST API translates a ‘quit’ POST to invoke quit on the main thread of the script.
I found a couple of SO questions that want to do something similar, but not exactly like this. I tried to follow the below example, but it didn’t work:
Python StringIO - selectively place data into stdin
Then I tried something simple. Basically did the below from the thread:
sys.stdin = StringIO('quit\n')
sys.stdin.flush()
It seems that the word 'quit' was injected, but the script would not do its clean exit. However, I could go back to the command line and simply press Enter, which would trigger the cleanup and exit process. Not sure why ‘\n’ was not being interpreted as Enter.
My cmd is simply invoked in the below manner:
mycmd = MyCmd()
mycmd.cmdloop()
How can I properly inject ‘quit’ into the cmdloop of Cmd?

Have you tried the cmd.onecmd("quit") method? I think it can be used to inject commands in the interpreter.

Related

How to remove "press enter to continue" when running Powershell within Python

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?

Interactively communicating with a FORTRAN shell program

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.

Executing shell command from python

I am trying to compile a set of lines and execute them and append the output to text file. Instead of writing the same thing, I used a python script to compile and execute in background.
import subprocess
subprocess.call(["ifort","-openmp","mod1.f90","mod2.f90","pgm.f90","-o","op.o"])
subprocess.call(["nohup","./op.o",">","myout.txt","&"])
The program pgm.f90 is getting compliled using the ifort compiler, but the ouput is not getting appended to myout.txt. Instead it is appending output to nohup.out and the program is not running in the background even after specifying "&" in the python script.
What obvious error have I made here?
Thanks in advance
You can call a subprocess as if you were in the shell by using Popen() with the argument shell=True:
subprocess.Popen("nohup ./op.o > myout.txt &", shell=True)
This issue is that when you supply arguments as a list of elements, the subprocess library bypasses the shell and uses the exec syscall to directly run your program (in your case, "nohup"). Thus, rather than the ">" and "&" operators being interpreted by the shell to redirect your output and run in the background, they are being passed as literal arguments to the nohup command.
You can tell subprocess to execute your command via the shell, but this starts a whole extra instance of shell and can be wasteful. For a workaround, use the built-in redirection functionality in subprocess instead of using the shell primitives:
p = subprocess.Popen(['nohup', "./op.o"],
stdout=open('myout.txt', 'w'))
# process is now running in the background.
# if you want to wait for it to finish, use:
p.wait()
# or investigate p.poll() if you want to check to see if
# your process is still running.
For more information: http://docs.python.org/2/library/subprocess.html

Python and subprocess input piping

I have a small script that launches and, every half hour, feeds a command to a java program (game server manager) as if the user was typing it. However, after reading documentation and experimenting, I can't figure out how I can get two things:
1) A version which allows the user to type commands into the terminal windoe and they will be sent to the server manager input just as the "save-all" command is.
2) A version which remains running, but sends any new input to the system itself, removing the need for a second terminal window. This one is actually half-happening right now as when something is typed, there is no visual feedback, but once the program is ended, it's clear the terminal has received the input. For example, a list of directory contents will be there if "dir" was typed while the program was running. This one is more for understanding than practicality.
Thanks for the help. Here's the script:
from time import sleep
import sys,os
import subprocess
# Launches the server with specified parameters, waits however
# long is specified in saveInterval, then saves the map.
# Edit the value after "saveInterval =" to desired number of minutes.
# Default is 30
saveInterval = 30
# Start the server. Substitute the launch command with whatever you please.
p = subprocess.Popen('java -Xmx1024M -Xms1024M -jar minecraft_server.jar',
shell=False,
stdin=subprocess.PIPE);
while(True):
sleep(saveInterval*60)
# Comment out these two lines if you want the save to happen silently.
p.stdin.write("say Backing up map...\n")
p.stdin.flush()
# Stop all other saves to prevent corruption.
p.stdin.write("save-off\n")
p.stdin.flush()
sleep(1)
# Perform save
p.stdin.write("save-all\n")
p.stdin.flush()
sleep(10)
# Allow other saves again.
p.stdin.write("save-on\n")
p.stdin.flush()
Replace your sleep() with a call to select((sys.stdin, ), (), (), saveInterval*60) -- that will have the same timeout but listens on stdin for user commands. When select says you have input, read a line from sys.stdin and feed it to your process. When select indicates a timeout, perform the "save" command that you're doing now.
It won't completely solve your problem, but you might find python's cmd module useful. It's a way of easily implementing an extensible command line loop (often called a REPL).
You can run the program using screen, then you can send the input to the specific screen session instead of to the program directly (if you are in Windows just install cygwin).

Retrieving Raw_Input from a system ran script

I'm using the OS.System command to call a python script.
example:
OS.System("call jython script.py")
In the script I'm calling, the following command is present:
x = raw_input("Waiting for input")
If I run script.py from the command line I can input data no problem, if I run it via the automated approach I get an EOFError. I've read in the past that this happens because the system expects a computer to be running it and therefore could never receive input data in this way.
So the question is how can I get python to wait for user input while being run in an automated way?
The problem is the way you run your child script. Since you use os.system() the script's input channel is closed immediately and the raw_input() prompt hits an EOF (end of file). And even if that didn't happen, you wouldn't have a way to actually send some input text to the child as I assume you'd want given that you are using raw_input().
You should use the subprocess module instead.
import subprocess
from subprocess import PIPE
p = subprocess.Popen(["jython", "script.py"], stdin=PIPE, stdout=PIPE)
print p.communicate("My input")
Your question is a bit unclear. What is the process calling your Python script and how is it being run? If the parent process has no standard input, the child won't have it either.

Categories