4

I want to do something similar to the second answer here (but not quite similar): Simulate Ctrl-C keyboard interrupt in Python while working in Linux

It's much simpler and I think I'm just missing something. Say, from a python script, I just want to call 'ping' and terminate it after the 10th time. I'm trying to do it like from the link above:

p = subprocess.Popen(['ping', 'google.com'], stdout=subprocess.PIPE)
for line in p.stdout:
  print line
  if re.search('10', line):
    break
os.kill(p.pid, signal.SIGINT)

But it doesn't work.

And I also want the regular output of 'ping' to be displayed. How do I do this?

EDIT: It's not actually a 'ping' that I want to do. I'm just using it as an example of a command with continuous output, which in time, I would like to terminate.

More specifically, I'm running an old version of BitTorrent (v5.0.9 from the 3rd answer here: Where to find BitTorrent source code?) and I'm calling it via python script. The bittorrent-console.py is a simple terminal version, hence 'console'. It outputs multiple lines periodically. Something like:

saving:       filename
file size:    blah
percent done: 100.0
blah:         blahblah

I'm actually calling it by:

subprocess.call(['./bittorrent-console.py', 'something.torrent'])

I want to terminate it automatically when I see it's 100.0 in 'percent done:'.

EDIT: I'm running on CentOS, Python 2.6.

6
  • 1
    note also that 10 might be part of the IP address Commented Sep 27, 2013 at 20:19
  • 1
    Yep, I realized that, but assume there's no 10 in the address or the time. But again, just an example. It could've been if re.search('icmp_seq=10'. Commented Sep 27, 2013 at 20:31
  • Note that subprocess.Popen and subprocess.call differ in one very important detail. The former is asynchronous, the latter is syncrhonous. In the latter case, by the time you call os.kill(), the app is already dead and os.kill() has no effect whatsoever. Commented Sep 27, 2013 at 21:20
  • Ah. Yes. That is indeed true. I want to keep on using call though, because there's no lag in output like when I tried the answer below. It simply has just called bittorrent-console and left it running. I don't know how to terminate it when the download's done except by Keyboard Interrupt. Commented Sep 27, 2013 at 21:45
  • Would you happen to know if inside the script, after call, there's a way to get the process id of what I just called? Commented Sep 27, 2013 at 22:02

2 Answers 2

3

Firstly, you want to use p.stdout.readline. I'm not sure why, but for line in p.stdout doesn't seem to flush. Perhaps it's buffered.

Secondly, you should use sys.stdout.write(line), because print always appends something. In Python 3 you can use print(line, end=""), though.

Also, you should prefer p.kill to os.kill. I'm not sure why you're using os.kill.

import os
import signal
import subprocess
import sys

p = subprocess.Popen(['ping', 'google.com'], stdout=subprocess.PIPE)
while True:
    line = p.stdout.readline()

    sys.stdout.write(line)
    sys.stdout.flush()
    if '10' in line:
        break

p.kill()
Sign up to request clarification or add additional context in comments.

4 Comments

I think this will do the trick. Worked for the ping example. I'll try it later on what I'm really working on.
This doesn't output very well. It takes time to write and flush per line since bittorrent-console prints out multiples lines at a time. Is there any way that I could just use subprocess.call, catch the '100.0' and terminate bittorrent-console?
No, there is not. You can pass bufsize=0 to subprocess.Popen but there is nothing more that you can do (parsing sub-lines could help if lines are given out incrementally, but I doubt it).
Well, your answer did solve my question. I don't want the slow update on terminal output though. I need it to still look real-time. I guess I'll have to find another way.
2

This does exactly what you want and works perfectly on my OS X machine:

import subprocess
import re

def get_output(cmd, until):
    p = subprocess.Popen(cmd, stdout=subprocess.PIPE)
    ret = []
    while True:
        line = p.stdout.readline()
        ret.append(line)
        if re.search(until, line):
            break
    p.kill()
    return ret

 print ''.join(get_output(['ping', 'google.com'], until='10'))

4 Comments

Again, 10 and ping were just examples.
I don't see how this answer doesn't answer your question.
Sure, it does. But you iterated over a range. I wanted to terminate when I see a pattern '10'.
I thought you meant "after 10 lines of output". In any case, I've updated my answer accordingly.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.