I want to run this command from python mentioned here:
ffmpeg -f concat -safe 0 -i <(for f in ./*.wav; do echo "file '$PWD/$f'"; done) -c copy output.wav
But i can't even run this:
subprocess.run('for i in {1..3}; do echo $i; done'.split(), capture_output=True)
Error:
Traceback (most recent call last):
File "/media/russich555/hdd/Programming/Freelance/YouDo/21.intercom_record/test.py", line 36, in <module>
pr = subprocess.run(
^^^^^^^^^^^^^^^
File "/usr/lib/python3.11/subprocess.py", line 546, in run
with Popen(*popenargs, **kwargs) as process:
^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.11/subprocess.py", line 1022, in __init__
self._execute_child(args, executable, preexec_fn, close_fds,
File "/usr/lib/python3.11/subprocess.py", line 1899, in _execute_child
raise child_exception_type(errno_num, err_msg, err_filename)
FileNotFoundError: [Errno 2] No such file or directory: 'for'
Process finished with exit code 1
Also tried add shell=True
:
subprocess.run('for i in {1..3}; do echo $i; done'.split(), capture_output=True, shell=True)
stderr output:
i: 1: Syntax error: Bad for loop variable
Also tried pass /bin/bash
, because documentation says that shell=True
using /bin/sh
subprocess.run('/bin/bash for i in {1..3}; do echo $i; done'.split(), capture_output=True)
stderr output:
/bin/bash: for: No such file or catalog
CodePudding user response:
There are two errors here, or really, three;
- You are trying to use shell features without
shell=True
- You are trying to use Bash features, but the default shell on non-Windows platforms is POSIX
sh
; you can fix that withexecutable='/bin/bash'
(obviously, adjust the path if necessary).
More fundamentally, though, you want to avoid using a subprocess when Python can perform the loop natively.
from pathlib import Path
import subprocess
subprocess.run(
['ffmpeg', '-f', 'concat', '-safe', '0',
'-i', '/dev/stdin', '-c', 'copy', 'output.wav'],
input="".join(f"file '{x}'\n" for x in Path.cwd().glob("*.wav")),
text=True, capture_output=True)
Relying on /dev/stdin
for the input file is somewhat platform-dependent; in the worst case, you'll need to refactor to use a temporary file, or fall back to using the shell after all.
subprocess.run(r"""ffmpeg -f concat -safe 0 -i <(printf "file '%s'\n" $PWD/*.wav) -c copy output.wav""",
shell=True, executable='/bin/bash',
text=True, capture_output=True)
As noted in comments, you should either use shell=True
and pass in a single string as the first argument for the shell to parse, or else pass in a list of tokens without shell=True
and with no shell features like wildcard expansion, command substitution, variable interpolation, redirection, shell builtins, etc etc.
If you really wanted to explicitly run Bash, the syntax for that would look like
subprocess.run(
['bash', '-c',
r"""ffmpeg -f concat -safe 0 -i <(printf "file '%s'\n" $PWD/*.wav) -c copy output.wav"""],
text=True, capture_output=True)
(The syntax bash for loop etc
tries to find a file named for
and run it with Bash, passing in loop
and etc
as arguments.)
It's not clear why you are using capture_output=True
here; in order for that to be useful, you need to examine the .stdout
(and/or perhaps .stderr
) attributes of the object returned by subprocess.run
. If you just want to discard the output, use stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL