xargs - multiple commands and rm at the end - shell

I'm trying to remove the file that I was working on previously but it's not letting me, please help. Here is the command I run:
find . -type f -name '*.flac' -print0 |
xargs -0i ffmpeg -i {} -acodec libmp3lame -ab 320k "{}.mp3" |
rm -rf {}

If you have GNU Parallel installed you can do:
find . -type f -name '*.flac' | parallel ffmpeg -i {} -acodec libmp3lame -ab 320k {.}.mp3 '&&' rm {}
It will run one ffmpeg process per CPU core.
To learn more watch the intro videos: http://pi.dk/1

You are piping the output of the ffmpeg call(s) into the rm command. Since ffmpeg produces no interesting output and rm does not read any input, this doesn't do anything.
I'm not sure what you're trying to do. I think you want to remove the flac file after processing. You have several choices: you can first convert all the ffmpeg files, then remove them all; or you can remove each file after it's been processed. I advise the latter, otherwise it will be difficult to only remove the flac file if the conversion succeeded.
Rather than use xargs, it's simpler to use find … -exec here. For each flac file, call ffmpeg, and then delete the file if ffmpeg succeeded. If your find doesn't have the -delete action, use -exec rm {} \; instead. Use an intermediate shell to construct the output file name.
find . -type f -name '*.flac' \
-exec sh -c 'ffmpeg -i "$0" -acodec libmp3lame -ab 320k "${0%.*}.mp3"' {} \; \
-delete
You can use the rm command inside the shell snippet instead.
find . -type f -name '*.flac' \
-exec sh -c 'ffmpeg -i "$0" -acodec libmp3lame -ab 320k "${0%.*}.mp3" && rm "$0"' {} \;
With some versions of find, if you want the output file to be called foo.flac.mp3, you can skip the intermediate shell.
find . -type f -name '*.flac' \
-exec ffmpeg -i {} -acodec libmp3lame -ab 320k {}.mp3 \; \
-delete

The command rm doesn't take input from standard input, so you need to send it input using xargs just like you did for ffmpeg
try this:
find . -type f -name '*.flac' -print0 |
xargs -0i bash -c 'ffmpeg -i {} -acodec libmp3lame -ab 320k \"{}.mp3" ; rm -rf {}'
This adds rm into the command xargs executes, essentially creating an inline shell script using bash -c.

First of all, "it's not letting me" is not the best way to describe the problem. You should post the error message or describe what happens.
Second, I don't quite get what you're trying to achieve, but you're not using rm with xargs, you just pipe something to rm. That's not how rm works.
find . -type f -name '*.flac' -print0 | xargs -0i rm "{}"
would probably work, for example. I'm not using rm -r here, because find only looks for files anyway.

Related

How do you convert to the same format an entire directory with subfolders using ffmpeg?

How to keep the file name and the extension the same and append _backup to the old file?
I have had tried this
find . -name "*.mp4" -exec bash -c 'for f; do ffmpeg -i "$f" -codec copy "${f%.*}.mp4"; done' -- {} +
but here the files would be overwritten.
I hope what I have requested is possible.
I suggest you append "_backup" to your input files first, then process the just renamed files with ffmpeg:
Simple for-loop to process files in current directory:
for f in *.mp4; do
mv "$f" "${f%.*}_backup.mp4"
ffmpeg -i "${f%.*}_backup.mp4" -c copy "$f"
done
#or single-line:
for f in *.mp4; do mv "$f" "${f%.*}_backup.mp4"; ffmpeg -i "${f%.*}_backup.mp4" -c copy "$f"; done
find to process files in current directory and sub directories:
find -name "*.mp4" -exec bash -c '
f="{}"
mv "$f" "${f%.*}_backup.mp4"
ffmpeg -i "${f%.*}_backup.mp4" -c copy "$f"
' \;
#or single-line:
find -name "*.mp4" -exec bash -c 'f="{}"; mv "$f" "${f%.*}_backup.mp4"; ffmpeg -i "${f%.*}_backup.mp4" -c copy "$f"' \;

Run command inside all folders and sub folders with specific extension

Currently, I'm trying run a command on my linux server (Ubuntu 16.04) to reencode all .mp4 files. But I have a lot of files inside folders and subfolders, and I'm going to waste a lot of time running these commands manually
Here is my script:
for D in ./*; do
if [ -d "$D" ]; then
cd "$D"
for i in *.mp4; do
ffmpeg -i "$i" -codec copy -acodec copy -shortest -map 0:v -map 0:m:language:eng "${i%.*}.mp4"
done
cd ..
fi
done
But, I'm getting this error:
Error: ENOENT: no such file or directory, stat '*.mp4'
How can I solve this?
Thank you.
Use find for this. It handles sub-directory recursion for you. Like so:
find . -type f -name "*.mp4" -exec ffmpeg -i {} -codec copy -acodec copy -shortest -map 0:v -map 0:m:language:eng {} \; -print
find will automatically process the current directory and all sub-directories
-type f: to search only files
`-name "*.mp4": only files with mp4 extension
-exec COMMAND {} \;: execute that command on all found files. {} will be replaced by the file name of each file. \; to end the command.
-print: will print which file was processed
As far as your code goes, there is a possibility that a directory will not have any "*.mp4" files, hence the no such file or directory, stat '*.mp4' error when you hit such a directory.
Example (no effect, only do echo ...):
$ find \
. \
-type f \
-name '*.mp4' \
-exec sh -c '
for src in "$#"; do
dst="${src%.*}.mkv"
echo ffmpeg ... "$src" ... "$dst"
done
' sh {} + \
-print \
;
You must replace echo ffmpeg ... "$src" ... "$dst" with your command-line for production use. You can remove -print from the example.

Use parameter expansions in a command run from "find | xargs" to prevent output overwriting

I have this bash script that is looking for mp4 files in subfolders with certain names and saves frames of those videos as jpeg.
#!/bin/bash
find ../folder -type f -iname '*C00*.mp4' | xargs -I %% ffmpeg -i %% -vf fps=1 -q:v 3 "../frames/_${i%.*}_frame%d.jpg"
The problem is that everytime the script finishes one video the .jepg output files of the next videos are overwriting the existing ones.
How can I prevent that?
Here's a quick stab which creates a directory with the same name as the input file with any .mp4 extension trimmed off.
#!/bin/bash
find ../folder -type f -iname '*C00*.mp4' -print0 |
xargs -r0 sh -c 'for f; do
d="../frames/${f%.[Mm][Pp]4}"
mkdir "$d" || { echo "$d already exists" >&2; exit 123; }
ffmpeg -i "$f" -vf fps=1 -q:v 3 "$d/frame%d.jpg"
done' _

How do handle filename space and Character with FIND command

Good day all,
I am working on a bash script to merge multiple mp3 files to one. The code is working fine but cannot handle file name with space or Characters. Below is the code. Can you please tell me what I'm doing wrong. Thanks
for file in $(find . -type f -name "*.mp3" -print0 | xargs -0 ); do
ffmpeg -i "concat:intro.mp3|"$file"|outro.mp3" -acodec copy "${file%.mp3}-".mp3;
done
find has an -exec flag that allows you to call scripts with the search results.
e.g. creating a simple helper-script addxtros.sh:
#!/bin/sh
infile=$1
outfile=${infile%.mp3}-.mp3
ffmpeg -i "concat:intro.mp3|${infile}|outro.mp3" -acodec copy "${outfile}"
you can use it like:
find . -type f -name "*.mp3" -exec ./addxtros.sh {} ";"
read builtin with -d followed by empty argument to use NUL caracter as input record delimiter
while IFS= read -r -d '' file; do
ffmpeg -i "concat:intro.mp3|$file|outro.mp3" -acodec copy "${file%.mp3}-".mp3;
done < <(find . -type f -name "*.mp3" -print0)
You can use find together with bash -c command (that allows passing 2 arguments to ffmpeg):
find . -type f -name "*.mp3" -exec bash -c 'ffmpeg -i "concat:intro.mp3|$1|outro.mp3" -acodec copy "${1%.mp3}-.mp3"' _ {} \;

Bash script to search and rename recursively

I have a bash script that converts *.mkv files to *.avi files. Here's what it looks like:
#!/bin/bash
for f in $(ls *mkv | sed ‘s/\(.*\)\..*/\1/’)
do
ffmpeg -i $f.mkv -sameq $f.avi
done
What I need this script to do however, is it needs to search recurssively in all folders for *.mkv files and then run the ffmpeg command and save the output to the same directory.
PLEASE can someone help me? :-)
find /some/path -name '*.mkv' | while read f
do
ffmpeg -i "$f" -sameq "${f:0:-4}.avi"
done
Try like this:
find <file_path> -name '*.mkv' -exec sh -c 'mv "$0" "${0%%.mkv}.avi"' {} \;
#!/bin/bash
find . -name "*.mkv" -exec ffmpeg -i {} -sameq `basename {} .mkv`.avi \;
Thanks to #Raul this is what worked for me and is the solution to what I wanted to do which is run recursively through directories and run the ffmpeg command on mkv files:
#!/bin/bash
find <file_path> -name '*.mkv' -exec sh -c 'ffmpeg -i "$0" -sameq "${0%%.mkv}.avi"' {} \;
exit;
Instead of ls *.mkv use find . -name "*.mkv".
This assumes no funny filenames (no spaces, newlines). Another possibility is using find in conjunction with xargs. The xargs manual makes for an instructive reading which will save your scripting life one day :-)

Resources