Python and subprocess input piping - python

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

Related

Writing input to a process opened with Popen

I have a program called my_program that operates a system. the program runs on Linux, and I'm trying to automate it using Python.
my_program is constantly generating output and is suppose to receive input and respond to it.
When I'm running my_program in bash it does work like it should, I receive a constant output from the program and when I press a certain sequence (for instance /3 to change the mode of the system), the program responds with an output.
to start the process I am using:
self.process = Popen(my_program,stdin=PIPE,stdout=PIPE,text=True)
And in order to write input to the system I am using:
self.process.stdin.write('/3')
But the writing does not seem to work, I also tried using:
self.process.communicate('/3)
But since my system constantly generating output, it deadlooks the process and the whole program gets stuck.
Any solution for writing to a process that is constantly generating output?
Edit:
I don't think I can provide a code that can reproduce the problem because I'm using a unique SW that my company has, but it goes somthing like this:
self.process = Popen(my_program,stdin=PIPE,stdout=PIPE,text=True)
self.process.stdin.write('/3')
# try to find a specific string that indicated that the input string was received
string_received = False
while(string_received = False):
response = self.process.stdout.readline().strip()
if (response == expected_string):
break
The operating system implements buffered I/O between processes unless you specifically request otherwise.
In very brief, the output buffer will be flushed and written when it fills up, or (with default options) when you write a newline.
You can disable buffering when you create the Popen object:
self.process = Popen(my_program, stdin=PIPE, stdout=PIPE, text=True, bufsize=1)
... or you can explicitly flush() the file handle when you want to force writing.
self.process.stdin.flush()
However, as the documentation warns you, if you can't predict when the subprocess can read and when it can write, you can easily end up in deadlock. A more maintainable solution might be to run the subprocess via pexpect or similar.

Is there any way to delay a Python Script till an Application is running?

I want the Python Code to open Notepad and let the user type in it, by the time it should not execute the rest of the code. After I close notepad, it should resume the script from where it left. Is there any way to do this? Or should I try a different approach?
What have I Tried:
Here is the code so far -
with open('file.txt','w') as file: #this is to create an empty file
file.close()
pass
os.startfile('file.txt')
time.sleep() # what value should I enter for time.sleep? or is there a module to do this?
I possibly can run a while loop to check whether the notepad.exe is running or not, if it is, if it's not, it should break out of the loop and execute the rest of the code.However, the problem is how do I check if notepad.exe is running?
Running a while loop to delete the file, if it get's an error, means the program is still running, but the problem is if it does not get the error, It will delete the file.
It would be better, if when launching of the program, it takes the process ID of it, and only wait for it to terminated. So that other instances of notepad won't be affected.
From the docs:
startfile() returns as soon as the associated application is
launched. There is no option to wait for the application to close, and
no way to retrieve the application’s exit status.
If you know the path of the application to open the file with, you could use subprocess.Popen() which allows for you to wait.
p = subprocess.Popen([
'C:\\Windows\\System32\\notepad.exe',
'path\\to\\file'
])
(output, err) = p.communicate()
#This makes the wait possible
p_status = p.wait()
See:
http://docs.python.org/library/os.html#os.startfile
http://docs.python.org/library/subprocess.html#subprocess.Popen

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.

Why does os.system block program execution?

I am trying to create a program to easily handle IT requests, and I have created a program to test if a PC on my network is active from a list.
To do this, I wrote the following code:
self.btn_Ping.clicked.connect(self.ping)
def ping(self):
hostname = self.listWidget.currentItem().text()
if hostname:
os.system("ping " + hostname + " -t")
When I run it my main program freezes and I can't do anything until I close the ping command window. What can I do about this? Is there any other command I can use to try to ping a machine without making my main program freeze?
The docs state that os.system() returns the value returned by the command you called, therefore blocking your program until it exits.
They also state that you should use the subprocess module instead.
From ping documentation:
ping /?
Options:
-t Ping the specified host until stopped.
To see statistics and continue - type Control-Break;
To stop - type Control-C.
So, by using -t you are waiting until that machine has stopped, and in case that machine is not stopping, your Python script will run forever.
As mentioned by HyperTrashPanda, use another parameter for launching ping, so that it stops after one or some attempts.
As mentioned in Tim Pietzcker's answer, the use of subprocess is highly recommended over os.system (and others).
To separate the new process from your script, use subprocess.Popen. You should get the output printed normally into sys.stdout. If you want something more complex (e.g. for only printing something if something changes), you can set the stdout (and stderr and stdin) arguments:
Valid values are PIPE, DEVNULL, an existing file descriptor (a positive integer), an existing file object, and None. PIPE indicates that a new pipe to the child should be created. DEVNULL indicates that the special file os.devnull will be used. With the default settings of None, no redirection will occur; the child’s file handles will be inherited from the parent.
-- docs on subproces.Popen, if you scroll down
If you want to get the exit code, use myPopenProcess.poll().

Python print buffering

Let me rephrase my previous question.
I just created a tool in ArcGIS using pythong as script language. The tool executes (runs) an outside program using the subprocess.popen. When I run the tool from ArcGSIS, a window appears that only shows the following.
Executing: RunFLOW C:\FLOW C:\FLOW\FLW.bat
Start Time: Mon Nov 30 16:50:37 2009
Running script RunFLOW...
Completed script RuFLOW...
Executed (RunFLOW) successfully.
End Time: Mon Nov 30 16:50:48 2009 (Elapsed Time: 11.00 seconds)
The script is as follows
# Import system modules
import sys, string, os, arcgisscripting, subprocess
# Create the Geoprocessor object
gp = arcgisscripting.create()
# Read the parameter values:
# 1: input workspace
prj_fld = gp.GetParameterAsText(0)
Flow_bat = gp.GetParameterAsText(1)
os.chdir(prj_fld)
p=subprocess.Popen(Flow_bat,shell=True,stdout=subprocess.PIPE)
stdout_value = p.communicate()[0]
print '\tstdout:', repr(stdout_value)
When I run the same program from command window, it prints a screen full of information (date, number of iteration, etc.). I want to see all this information in the window that appears after I run the model from ArcGIS in addition to what it is being printing right now.
I tried print, communicate, flush but couldn't be able to do it. Any suggestions?
When I run the script as it is right now, it runs the executable but it gives an error as follows
ERROR 999998: There are no more files.
Thanks
I know nothing about ArcGIS, so I may be shooting in the dark here, but...if you want stdout, you usually don't want the communicate() method. You want something like this:
p=subprocess.Popen(Flow_bat,shell=True,stdout=subprocess.PIPE)
stdout_value = p.stdout.read()
The communicate() method is used for interacting with a process. From the documentation:
Interact with process: Send data to stdin. Read data from stdout
and stderr, until end-of-file is reached. Wait for process to
terminate.
I'm guessing that when ArcGIS runs your script that stdin is not connected and causes the script to exit for some reason.
I'm not sure if you are aware of this or not but I use:
gp.AddMessage('blah blah')
to get messages to appear in the ArcGIS processing window. Its a method of the geoprocessor object, the library you import in Python to access the ArcGIS engine. You would already have imported this library if you are doing any geoprocessing.

Categories