Subprocess for python loop of commands [duplicate] - python

I have created a simple method that executes a command like you do in the terminal
from subprocess import PIPE, run
class Command_Line():
#staticmethod
def execute(command):
result = run(command, stdout=PIPE, stderr=PIPE, universal_newlines=True, shell=True)
print(result)
return result.stdout
My problem with the code above is it does not wait until the task/process is done. Lets say i use ffmpeg to change the frame rate of a video via the following code
import Command_Line as cmd
cmd.execute('ffmpeg -i "000000004.avi" -c copy -y -r 30 "000000004.avi"')
The problem is the output video because it does not complete the process. I've search how to wait like Is there a way to check if a subprocess is still running?
but could not incorporate it with my code. Can you share your experience with this.
Thanks

According to the python documentation subprocess.run waits for the process to end.
The problem is that ffmpeg overwrites the input file if the input and output files are the same and therefore the output video becomes unusable.

subprocess.run() is synchronous which means that the system will wait till it finishes before moving on to the next command. subprocess.Popen() does the same thing but it is asynchronous (the system will not wait for it to finish). You can try reloading your file using importlib.reload command. It may find your generated file then.

Related

How to run cmd.exe in python

I'm trying to port the following c# code into Python. It firstly defines a new process and then runs a windows prompt command (cmd.exe). After that, it executes a command in the prompt and when an external event occurs, it closes the prompt.
//Start the prompt - when an event occured
Process winShell = new Process();
winShell.StartInfo.FileName = "cmd.exe";
winShell.StartInfo.RedirectStandardInput = true;
winShell.Start();
//Execute a command in the prompt
winShell.StandardInput.WriteLine("cd " + projectDirectory);
//Close it - when an event occured
winShell.StandardInput.Flush();
winShell.StandardInput.Close();
winShell.WaitForExit();
I read that for Python 3 (my version 3.7), It is recommended to use subprocess. Unfortunately, I feel a bit confused about which of the function to use. I found call, run and Popen, but I didn't understand how to use them.
I wrote the following lines, but they don't produce any visible result.
import subprocess
subprocess.run(['cmd.exe'])
First of all, I would like that the shell appears and than to write some commands in it. Finally, I want to close it.
Use subprocess.Popen() like this. Each API matches to the corresponding C# API almost 1:1.
p = subprocess.Popen(['cmd.exe'],
stdin=subprocess.PIPE, stdout=subprocess.PIPE,
text=True)
p.stdin.write('dir\n')
p.stdin.close()
print(p.stdout.read())
p.wait()
p.stdout.close()
Other API's such as run(), call(), etc are wrappers for Popen(). For example, the above code is equivalent to this one line.
print(subprocess.run(['cmd.exe'], capture_output=True, text=True, input = 'dir\n').stdout)

subprocess Popen stdin will only input and run after the script has finished

Description:
I was trying to make a shell that can be interactive on a chatting software, so I need a cmd.exe as a subprocess and pass strings into the process.
I have this:
from subprocess import Popen
from subprocess import PIPE as p
proc = Popen("cmd",stdout=p,stdin=p,shell=True)
so usually what we do if we need to pass input to the process is by using proc.stdin.write()
but it seems that the string will only pass in and work after the python script is complete
for example, I have
#same thing above
proc.stdin.write("ping 127.0.0.1".encode())
time.sleep(10)
the script will wait for 10 sec then pass and run the ping command.
which means it's impossible to get the result stdout.read() because there is nothing.
I have tried to use subprocess.Popen.communicate() but it closes the pipe after one input.
Is there any way to solve the "only run the command after script finish" thing, or make communicate() not close the pipe?
Writes to pipes are buffered, you need to flush the buffer.
proc.stdin.write("ping 127.0.0.1".encode())
proc.stdin.flush()

Why do two sub-processes stop each other from working?

I'm having a problem getting two subprocesses to run together.
The first subprocesss is a transcode of a video stream:
subprocess.Popen("ffmpeg -i input output", shell=True)
I need this to run in the background of my program, transcoding video from my IP camera into a mjpeg stream.
The second subprocess is the Openalpr Daemon that looks at the mjpeg stream and returns car license plates it sees in the stream.
subprocess.Popen("alprd -f", shell=True)
Here is a sample piece of python test code that tries to run both subprocesses:
import subprocess
subprocess.Popen("ffmpeg -i input output", shell=True)
subprocess.Popen("alprd -f", shell=True)
When i do this, the ffmpeg transcoding works fine, i can view the transcoded mjpeg stream and i can see ffmpegs verbose output in the console. However, the alprd daemon seems to not return any number plates as expected. In fact, i can't see any output from alprd in the console window.
If i run the above code with just one subprocess it works e.g.
import subprocess
subprocess.Popen("ffmpeg -i input output", shell=True)
works fine, as does:
import subprocess
subprocess.Popen("alprd -f", shell=True)
If i run either of the two working code snippets above - and at the same time run the other command in a separate linux terminal, it all works.
I'm clearly not understanding something with subprocesses, They are clearly conflicting with each other, but Can anyone explain what is happening and how to resolve the problem?
Thanks!
It's likely that ffmpeg and alprd are both trying to interact with the same stdin/stdout file handles. To solve this, create separate pipes for one or both subprocesses to use as stdin/stdout. Then they can interact with them without interfering with each other.
import subprocess
with open('ffmpeg-output.txt', 'w') as ffmpeg_output:
ffmpeg = subprocess.Popen(
['ffmpeg', '-i', 'input', 'output'],
stdin=subprocess.PIPE,
stdout=ffmpeg_output,
stderr=subprocess.STDOUT)
# We won't be sending any input into ffmpeg's stdin, so close it.
ffmpeg.stdin.close()
# alprd inherits stdin, stdout, and stderr from the current process.
alprd = subprocess.Popen(['alprd', '-f'])
# Wait for the subprocesses to finish.
ffmpeg.wait()
alprd.wait()

Python, mpg123 and subprocess not properly using stdin.write or communicate

ok, so my google-fu really kind of sucks and I was unable to find an answer, hopefully you folks can help me ^_^
ok, so what I thought would be a simple script is seemingly not communicating with its subprocess correctly, I'm running this line by line. I'm also using the mpg123 player, this is a Linux system (well, Raspberry Pi)
from subprocess import Popen, PIPE, STDOUT
p = Popen(["mpg123", "-C", "test.mp3"], stdout=PIPE, stdin=PIPE, stderr=STDOUT)
#wait a few seconds to enter this, "q" without a newline is how the controls for the player work to quit out if it were ran like "mpg123 -C test.mp3" on the command line
p.communicate(input='q')[0]
I can run stdout.read() on it just fine, but using communicate for input just makes it hang and p.stdin.write('q') does seemingly nothing at all. This is python related though I have a feeling I'm also not looking in the right place in the mpg123 documentation. Please be kind as I'm exceptionally new to this ^_^
Check what arguments your mpg123 version understands. The following works on my machine:
#!/usr/bin/env python3
import time
from subprocess import Popen, PIPE, DEVNULL, STDOUT
p = Popen(["mpg123", "-K", "test.mp3"], stdin=PIPE, stdout=DEVNULL, stderr=STDOUT)
# wait a little
time.sleep(3)
# send command "Skip song", wait for the player to exit
p.communicate(b'n')[0]
It starts playing the file, waits ~3 seconds, and exits.
This is an awful solution, but it works in a pinch. I'm using this as a patch because for some reason, I cannot get Python libraries to play properly on my Raspberry Pi.
If you start mpg123 in remote mode (mpg123 -R), you can send commands to it far more easily:
proc = sp.Popen(["mpg123", "-R"], stdin=sp.PIPE)
Then, you can send commands to its stdin attribute.
Note:
The commands are different. To pause, it's "pause", not " " for example. Run mpg123 -R in a console, then send it the help command to see a list of commands.
The commands need to be newline terminated.
From the docs:
-R, --remote
Activate generic control interface. mpg123 will then read and execute commands from stdin. Basic usage is ''load '' to play some file and the obvious ''pause'', ''command. ''jump '' will jump/seek to a given point (MPEG frame number). Issue ''help'' to get a full list of commands and syntax.

Sending commans on sub process in python

I just want to build a little python music client on my raspberry pi. I installed "mpg321" and it works great but now my problem. After sending the command
os.system("mpg321 -R testPlayer")
python waits for user input like play, pause or quit. If I write this in my terminal the player pause the music oder quits. Perfect but I want python to do that so I send the command
os.system("LOAD test.mp3")
where LOAD is the command for loading this mp3. But nothing happens. When I quit the player via terminal I get the error:
sh: 1: LOAD: not found
I think this means that
os.system("mpg321 -R testPlayer")
takes the whole process and after I quit it python tries to execute the comman LOAD. So how do I get these things work together?
My code:
import os
class PyMusic:
def __init__(self):
print "initial stuff later"
def playFile(self, fileName, directory = ""):
os.system("mpg321 -R testPlayer")
os.system("LOAD test.mp3")
if __name__ == "__main__":
pymusic = PyMusic()
pymusic.playFile("test.mp3")
Thanks for your help!
First, you should almost never be using os.system. See the subprocess module.
One major advantage of using subprocess is that you can choose whatever behavior you want—run it in the background, start it and wait for it to finish (and throw an exception if it returns non-zero), interact with its stdin and stdout explicitly, whatever makes sense.
Here, you're not trying to run another command "LOAD test.mp3", you're trying to pass that as input to the existing process. So:
p = subprocess.Popen(['mpg321', '-R', 'testPlayer'], stdin=PIPE)
Then you can do this:
p.stdin.write('LOAD test.mp3\n')
This is roughly equivalent to doing this from the shell:
echo -e 'LOAD test.mp3\n' | mpg321 -R testPlayer
However, you should probably read about communicate, because whenever it's possible to figure out how to make your code work with communicate, it's a lot simpler than trying to deal with generic I/O (especially if you've never coded with pipes, sockets, etc. before).
Or, if you're trying to interact with a command-line UI (e.g., you can't send the command until you get the right prompt), you may want to look at an "expect" library. There are a few of these to choose from, so you should search at PyPI to find the right one for you (although I can say that I've used pexpect successfully in the past, and the documentation is full of samples that get the ideas across a lot more quickly than most expect documentation does).
You are looking for a way to send data to stdin. Here is an example of this using Popen:
from subprocess import Popen, PIPE, STDOUT
p = Popen(['mpg321', '-R testPlayer'], stdout=PIPE, stdin=PIPE, stderr=STDOUT)
mpg123_stdout = p.communicate(input='LOAD test.mp3\n')[0]
print(mpg123_stdout)
You establish pointers to stdin and stdout, then after you start your process, you communicate with stdin and read from stdout. Be sure to send new lines (carriage returns)

Categories

Resources