4

I'm running a program from the command line. This program prints out some of my CPU's statistics (link to program if anyone's interested: https://github.com/opcm/pcm). The program doesn't exit. It'll print updated stats periodically in the same terminal.

I'm trying to use these stats that the program is retrieving in another program that I'm writing myself. The program I'm writing is just supposed to graph some of these CPU statistics vs. time in real time in python.

I'm not sure how to retrieve and use the stats being printed to the console in my program in real time. I know I can direct output to a text file instead of to the console by typing ./program.x > output.txt.

My first thought is to push the outputted statistics to a text file, and simultaneously be reading from that text file in my program. But this feels like a brute force method, and I'm not sure it'll work.

Does anyone know how I can use the real-time output from one program in another program?

Mr.Mips
  • 379
  • 2
  • 18
  • This sounds like a job for pipes. Have you considered pipes for your use case? – Charles Landau Jan 10 '19 at 01:40
  • @CharlesLandau I've never heard of or used pipes. I'll look up what they are/how they're used. – Mr.Mips Jan 10 '19 at 01:41
  • I'll post an answer with pipes – Charles Landau Jan 10 '19 at 01:42
  • 1
    Indirect duplicate of: https://stackoverflow.com/questions/375427/non-blocking-read-on-a-subprocess-pipe-in-python , because the output should be read from a subprocess. – Kingsley Jan 10 '19 at 01:44
  • @Kingsley I looked for similar questions but couldn't find anything. Perhaps the wording of my question was the issue and why I wasn't getting relevant posts (or just my lack of knowledge of pipes). My apologies. – Mr.Mips Jan 10 '19 at 01:46

3 Answers3

1

You can achieve this result in more than one way.

On a unix system the easiest one i believe would be the use of pipes Lets say you have program1 (p1) that outputs some data and program2(p2) that reads and use this data.

You could pipe the output of p1 to p2 as suggested by @Charles Landau using | which creates a pipe between the stdout and stdin of teh 2 programs

./p1 | p2

If you want to also visualize the output of p1 you could use tee which take the stdin and forks it to the stdout and to a program

./p1 | tee p2

Now This would work only if you intend to launch p1 and p2 at the same time (at which point a believe it would be better to make it a single program). A more generic use of pipe would be for p1 to create a named pipe (FIFO) and for p2 to read from this pipe:

p1.py (server)

import os
from random import randint
import time

FIFO="/tmp/test_fifo"

if not os.path.exists(FIFO):
    os.mkfifo(FIFO)

pipe = os.open( FIFO, os.O_RDWR) #I use this to avoid the program hanging on this step.
                                 #When you open a FIFO, the behavior is blocking, meaning that the program will hang until both end (r/w) of the pipe are being used
f = os.fdopen( pipe, "w")        #Then i want a file object so i can flush it

while 1:
    # Do operation
    output = randint(0,999999)
    # Print output
    print(output)

    # Send output into pipe
    f.flush()
    f.write( str(output) + '\n')
    time.sleep(0.5)

f.close()
pipe.close()

os.unlink(FIFO)

p2.py (client)

import os
import sys

FIFO="/tmp/test_fifo"

if not os.path.exists(FIFO):
    exit()

f = open( FIFO, "r")

while 1:
    read_fifo = f.readline()
    print( read_fifo, end='')
    sys.stdout.flush()

You can do some simple testing on FIFO's in linux also using the shell

mkfifo 1
shell1: tail -f 1
shell2: echo "Hello!" > 1

In a similar manner you could use the TCP protocol for the 2 program to communicate See python https://docs.python.org/3/library/socket.html for more info on this method

Crivella
  • 897
  • 6
  • 11
  • See I'm using PCM tools to get CPU power statistics as p1, and my own as p2. I can't really write a single program to do this because I don't think I'm supposed to edit PCM's source code. – Mr.Mips Jan 12 '19 at 02:14
  • @Mr.Mips I see that the code you are using also offers an API, which means that it should be possible to import it as a module and use its function calls to write your own wrapper for its output. – Crivella Jan 12 '19 at 06:19
  • @Mr.Mips Another possibility you could try is using something like mkfifo name_of_fifo; ./pcm | tee name_of_fifo and than use the second program to read from this fifo. The only problem is that the output will be flushed only when the FIFO buffer is full. Another problem is that depending on the program behavior the command might hang until you also start reading from the FIFO. (For example mkfifo 1; python > 1 and in shell2: tail -f 1 will not block, but will start printing output only after the FIFO has been filled with something like while 1: print("HI")) – Crivella Jan 12 '19 at 06:29
0

It sounds like you want pipes. Pipes take the standard output of a program and pass it directly to the next. The absolute simplest example I can think of:

ls | grep ".bin"

It's easy to remember because the pipe operator kinda looks like a pipe |

More about pipes: http://web.cse.ohio-state.edu/~mamrak.1/CIS762/pipes_lab_notes.html

Edit: using sys.stdin to read pipes forever is explained well here

The important pattern to take from this is right there:

import sys
while True:
    line = sys.stdin.readline()
    # do something with line
Charles Landau
  • 4,187
  • 1
  • 8
  • 24
  • how would I grab the input in my program from the pipe that's getting the other program's output in python? – Mr.Mips Jan 12 '19 at 02:43
  • 1
    @Mr.Mips the short answer is `sys.stdin` and I will edit my answer above – Charles Landau Jan 12 '19 at 04:18
  • So I made two programs for testing purposes. One prints numbers to the console (called Runner) and the other's code is exactly what you gave me except I added a `print(line)` portion in the while loop (called Capture). When I run `Runner | Capture`, where Runner and Capture are the compiled files from my code, it won't work and says "Runner and Capture commands not found". How would I use the pipes with my own compiled programs? – Mr.Mips Jan 13 '19 at 01:11
  • 1
    That would be a separate question you could post on SO, but it sounds like your programs aren't in PATH – Charles Landau Jan 13 '19 at 02:26
0

You can make something like tail in python. This is sample of code but can't handle if the file is deleted.

def tail_F(some_file):
    while True:
        try:
            for line in sh.tail("-f", some_file, _iter=True):
                yield line
                # do whatever with new line
        except sh.ErrorReturnCode_1:
            yield None