0

I am working on an Embedded application which uses some precompiled binaries from the CANBOAT repository.

There are two binaries available for usage:

  1. actisense-serial
  2. analyzer

In a shell, one requires to execute actisense -r /dev/ttyUSB0 | analyzer -json to obtain information from a device connected to the USB port. The above mentioned command dumps JSON information to STDOUT.

Sample output:

{"timestamp":"2018-08-30T16:27:23.629Z","prio":2,"src":3,"dst":255,"pgn":128259,"description":"Speed","fields":{"SID":106,"Speed Water Referenced Type":"Paddle wheel"}}

{"timestamp":"2018-08-30T16:27:23.629Z","prio":2,"src":6,"dst":255,"pgn":128259,"description":"Speed","fields":{"SID":106,"Speed Water Referenced Type":"Paddle wheel"}}

The above mentioned values keep being displayed on the STDOUT.

I wish to use the above mentioned shell commands in a python script to obtain the JSON values, to parse them and save them to a database.

Initially I want to start out with subprocess.check_output.

I tried:

import subprocess

if __name_ == "__main__":

     while True:
         value = subprocess.check_output(['actisense-serial -r /ttyUSB0',
                                           '|',
                                           'analyzer -json'],
                                        shell=True)
         print(value)

But there is no output available. I am not sure how do I route the output of the STDOUT to the check_output.

How do I achieve this, where the continuous JSON information coming from the shell commands can be parsed and used further in the application?

Shan-Desai
  • 2,331
  • 1
  • 33
  • 62
  • I'd start by fixing the command so it *actually works* before worrying about streaming the output. `shell=True` doesn't work the way you expect here -- only the first list element is parsed as a shell script. – Charles Duffy Aug 30 '18 at 17:35
  • 1
    `['''actisense-serial -r "$1" | analyzer -json''', '_', "/ttyUSB0"]` is an example of an argument list that would work with `shell=True` (the first list entry is the shell script to run, the second is the `$0` that script is run with, the third is the `$1` it's run with). – Charles Duffy Aug 30 '18 at 17:44
  • ...actual *continuous streaming* is a separate issue, and one we already have Q&A for. – Charles Duffy Aug 30 '18 at 17:44
  • 1
    https://stackoverflow.com/questions/46592284/reading-stdout-process-in-real-time is a Windows-y answer to the directly-asked question; https://stackoverflow.com/questions/2715847/python-read-streaming-input-from-subprocess-communicate is a more generic one. – Charles Duffy Aug 30 '18 at 17:51

1 Answers1

1

You can pass in a pipe to stdout and stderr when you're using Popen like this:

actisense_proc = subprocess.Popen(['actisense-serial', '-r', '/ttyUSB0'], 
                                  stdout=subprocess.PIPE)
analyzer_proc = subprocess.Popen(['analyzer', '-json'], stdin=actisense_proc.stdout,
                                 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
while analyzer_proc.poll() is None:
    print(analyzer_proc.stdout.readline())

Also note that instead of using shell=True, I used two Popen calls and piped the stdout of the first into the stdin of the second.

EDIT: Missed the streaming part of the question. Updated so it will constantly read from the stdout pipe. This will run until the subprocesses terminate though.

wholevinski
  • 2,978
  • 13
  • 19
  • 1
    This addresses a different problem in the OP's code, but I'm not convinced that it's relevant to the question that's directly asked; `communicate()` is collecting content all at once rather than reading a continuous stream, which is what the question specifically asks for. – Charles Duffy Aug 30 '18 at 17:47
  • ...for the question answered here, https://stackoverflow.com/questions/10405515/piping-in-shell-via-python-subprocess-module is a duplicate – Charles Duffy Aug 30 '18 at 17:53
  • @CharlesDuffy Yikes, totally missed that streaming part. – wholevinski Aug 30 '18 at 17:53