0

I am trying to capture and/or remove the output of a command launched by a os.system() call. The script will run under Linux and Windows.

I cannot use the subprocess module because the said command is interactive (i.e. : the user can type instructions to trigger various actions). So please do not mention this thread as a duplicate of the common questions asked for instance in :

  1. Python: How to get stdout after running os.system?
  2. How to store the return value of os.system that it has printed to stdout in python?
  3. Assign output of os.system to a variable and prevent it from being displayed on the screen
  4. ...

One solution could be to make subprocess work with such a "wrapped" program, but this seem quite a complicated task, and I want to keep the solution simple (no external module or 1000-line snippet...) as it is not a primary functionality of my script. The following threads seemed promising, but they do not work as well as a crude os.system() (nor are they as simple... ) :

  1. Running an interactive command from within python
  2. Non-blocking read on a subprocess.PIPE in python
  3. http://log.ooz.ie/2013/02/interactive-subprocess-communication-in.html

Another solution could be to work out a "tee" function such as the one natively supported in Linux distribs. I found a good implementation here for example (a Tee class which modifies sys.stdout to write both to a file and to the original sys.stdout) :

The problem is that os.system() does not seem to print to the main script stdout. Instead, it launches the program in a subshell and I cannot find a way to retrieve/suppress its output...

If you have any other approach or solution, please let met know. Thanks.


Some details about the context were asked and given in the comments below. The main interrogation bears upon why I stick with os.system() when subprocess seems to be the obvious solution.

The program I execute is called CAST3M (http://www-cast3m.cea.fr/). It is a finite-element code used to solve problems in various fields of physics. There is no GUI, so the user interacts via a command-line custom language called GIBIANE. Classically, you can either feed CAST3M with a pre-written GIBIANE data file, or launch the program without a data file an enter commands on-the-fly. Here are typical GIBIANE instructions (they define some points, then a line, a square and finally a cube lying upon them) :

OPTI 'DONN' 3 'ELEM' 'CUB8' ;
PT1 = 0. 0. 0. ;
PT2 = 1. 0. 0. ;
PT3 = 0. 1. 0. ;
PT4 = 0. 0. 1. ;
NN1 = 5 ;
DR1 = PT1 DROI NN1 PT2 ;
SF1 = DR1 TRAN NN1 PT3 ;
VL1 = SF1 VOLU 'TRAN' NN1 PT4 ;

I made a wrapper in Python meant to tweak some functionnalities of CAST3M before actually launching it. I need to log what is printed by this Python script as well as the output of the CAST3M session. When there is no interactivity, subprocess does the job. When there is interactivity, I am forced to use os.system() because subprocess works poorly with CAST3M (I just need to hand it over to CAST3M, which os.system() does out-of-the-box, at the expense of IO control that's true)

Community
  • 1
  • 1
gromk13
  • 241
  • 3
  • 10
  • 1
    Why do you think `subprocess` is the wrong tool for this task? It lets you interact with the process *or* leave the stdin in place, but still also suppress the output. `os.system()` is certainly **not** the tool, it cannot do what you want. – Martijn Pieters Oct 29 '13 at 15:08
  • You say that you want to "capture and/or remove the output", but also that the "command is interactive". Can you clarify? If the output is removed, how is the user intended to interact with the program? Does the user still need to see *some* of the output? Or is it okay for the user to type commands blind? – Weeble Oct 29 '13 at 15:12
  • What is the script you are running from `os.system()`? You could potentially change that script to write to a file and then read the file in your python script upon completion. – Rod Oct 29 '13 at 15:34
  • What, precisely, do you mean by the terms "capture" and "remove"? – Robᵩ Oct 29 '13 at 15:40
  • Okay, I'll precise the context a little bit more. I am actually writing a python wrapper for a finite-element software called CAST3M (http://www-cast3m.cea.fr/). It is a command-line "toolbox" which allows to create a mesh, a datafield, solve a linear system... by typing instructions such as "CHPO1 = RESO MAT1 CHPO2 ; " then "CHPO3 = CHPO1 * 5. ; ", that sort of things... – gromk13 Oct 29 '13 at 15:59
  • My wrapper does some pre-processing before actually launching CAST3M. I need to log to a file basically all what has been printed to my screen : python print() results before the os.system() call, and then the CAST3M output. So this is what I called "capture". For the "remove" part, let's consider this a bonus, as it only consists in dropping some part of CAST3M printings. – gromk13 Oct 29 '13 at 16:09
  • Again, I perfectly know how to do this when the session is not interactive, subprocess does the job. – gromk13 Oct 29 '13 at 16:12
  • @Martijn Pieters Actually os.system() **is** the tool for launching CAST3M interactively without worrying for full buffers or blocking threads... I fear that by using subprocess I will be bound to reprogram a complete "command prompt" system while CAST3M already has a fully functional one ! – gromk13 Oct 29 '13 at 17:15
  • @gromk13: sure, be stubborn; `os.system()` creates a subshell. You don't have **any** access to the stdin and stdout of the process, all you get is the exit value of the process after it completes. That makes `os.system()` the wrong tool for your job. – Martijn Pieters Oct 29 '13 at 17:32
  • @Martijn Pieters Then proove me that subprocess is suitable for what I have to do (that would be more constructive than granting me a -1 or just answer "don't use os.system() it is **no** good"...). os.system() works seamlessly with CAST3M, subprocess does not. Having CAST3M work is obviously of higher priority than logging its output to a file. I thought I had made that point quite clear, but apparently not... – gromk13 Oct 30 '13 at 09:13
  • @gromk13: You have provided little information on what you want to *do* with the process. Do you want your python code to interact with it? Send commands, then capture the output? Or should the output 'fall through' to the end user of your python program? What kind of commands are you sending? Without that information it is *very* hard to give you a decent answer. All I can tell you with the information you've given that `os.system()` **is not it**. – Martijn Pieters Oct 30 '13 at 09:38
  • @Martijn Pieters I edited my question to bring up some context information. I did not mean to look disrespectful or stubborn, I just did not realize these details were relevant. However, I am likely to keep using os.system() as my nth attempt to make subprocess work solded out by another failure. So I would rather give up the logging feature for interactive sessions... Thank you anyway. – gromk13 Oct 30 '13 at 17:18
  • @gromk13: Still not that clear to me what exactly an interaction with the process looks like, but I suspect that [`pexpect`](http://www.noah.org/wiki/pexpect) is what you are looking for; it lets you interact with a process through stdin/stdout, look for specific responses and send back commands based on that. – Martijn Pieters Oct 30 '13 at 17:22
  • @Martijn Pieters I guess the misunderstanding comes from you believing the interaction is between Python and CAST3M whereas I was talking about an interaction between CAST3M and the user all the while. This is what I meant when I said Python needs to "hand it over to CAST3M" : the script has done its job and has to let CAST3M do its own (be it in a subshell, it does not matter here), but I would have liked the main script to record CAST3M output (= user instructions and CAST3M answers). – gromk13 Oct 30 '13 at 18:44
  • @gromk13: Right, which is a pass-through setup. You cannot do that with `os.system()`, as it doesn't give you access to `stdin` or `stdout` of the spawned process. `subprocess` *would* but you'd need to set up a `select` loop to proxy between your own stdin and the CAST3M stdin, and the same for stdout in the other direction. I have never done this myself, so I cannot give you a complete answer for that. – Martijn Pieters Oct 30 '13 at 18:48
  • possible duplicate of [Wrap subprocess' stdout/stderr](http://stackoverflow.com/questions/4335587/wrap-subprocess-stdout-stderr) and [Can you make a python subprocess output stdout and stderr as usual, but also capture the output as a string?](http://stackoverflow.com/q/12270645) – Martijn Pieters Oct 30 '13 at 18:49
  • @Martijn Pieters Pexpect may help me to achieve these functionalities I am looking for, but with 2 unacceptable drawbacks : 1) it is an additional module, and I want to keep my script extra-light 2) it would kind of add a layer of interaction : "user<>CAST3M" would become "user<>Python<>CAST3M", which could lack robustness and proove buggy (cf. my pudic reference to "full buffers or blocking threads" in a previous comment) – gromk13 Oct 30 '13 at 18:54
  • @gromk13: unfortunately, if you want to record the full interaction with CAST3M you will **have** to act as a proxy. `subprocess` is then the way to achieve that. – Martijn Pieters Oct 30 '13 at 18:55
  • @Martijn Pieters Okay, at last we are on the same wavelength :) I will definitely take care to be more specific in my future questions on SO ! I do not know of a way to grant you points when you did not properly answer the question, but I will complete my own answer below and refer to your conclusions if this can be of any help to future visitors of this question... – gromk13 Oct 30 '13 at 19:02
  • @gromk13: don't worry about points (I care more about the site and making sure people get the right answers). I think the problem here was clarity as to what you wanted; there were too many signposts to what you *didn't* want here for that to be clear. :-) – Martijn Pieters Oct 30 '13 at 19:04

2 Answers2

1

=> As Martijn Pieters says, there is no way to retrieve the stdout of a os.system() call (because it spawns a subshell = a black box of which we know nothing but its return code). The solution is then to make Python act as a proxy between the final user and CAST3M (pass-through configuration : Python is the "middleman" listening to the user request and transmitting it "as-is" to CAST3M, then capturing CAST3M answer and printing it back to the user). For this, you have to use subprocess, threading and queue modules. If you do not mind the complexity it brings (and the alteration of the original HCI experience), here is a quick summary of links which may reveal usefull :

  1. Wrap subprocess' stdout/stderr
  2. Can you make a python subprocess output stdout and stderr as usual, but also capture the output as a string?
  3. Running an interactive command from within python
  4. Non-blocking read on a subprocess.PIPE in python
  5. http://log.ooz.ie/2013/02/interactive-subprocess-communication-in.html

=> Robᵩ provides a workaround for Linux only, by appointing the logging task to the "script" Linux tool. This allows to keep the "user<>CAST3M" interactivity untouched (no proxy here).

Community
  • 1
  • 1
gromk13
  • 241
  • 3
  • 10
0

On Linux, the script provides an interactive environment while capturing all user interaction:

os.system("script -c '/bin/ed /etc/passwd' /tmp/capture_file")

The above function call will invoke the line editor ed on the password file. All of the user interaction will be stored in /tmp/capture_file.

Robᵩ
  • 163,533
  • 20
  • 239
  • 308
  • Thank you for this answer. However, I really need the solution to work on both Linux and Windows. On Linux, I could also launch "castem > /tmp/capture_file 2>&1" instead of "castem" and then parse the file content as I want... – gromk13 Oct 29 '13 at 16:17
  • No, according to your comments, you could not run `castem > capture_file`. You said that the program was interactive and the user could type commands at it. Shell output redirection would prevent the user from seeing the prompts or results. On the other hand, **if** you could use `>`, then you could also use `subprocess.Popen(stdout=capture_file)`. – Robᵩ Oct 29 '13 at 16:24
  • You are totally right, I should have thought twice... But I still need a Windows-compliant solution ;) – gromk13 Oct 29 '13 at 16:27