Bash script to search and rename recursively - bash

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 :-)

Related

mac terminal ffmpeg batch recursive conversion preserving directory structure

i'm using ffmpeg on mac to batch convert .flv to .mp4 files. i'm trying to find all files in subdirectories of the current directory and save new files in the same directory.
for instance starting with:
subdirectory1/video1.flv
subdirectory1/video2.flv
subdirectory2/video1.flv
and ending with
subdirectory1/video1.mp4
subdirectory1/video2.mp4
subdirectory2/video1.mp4
i've gotten this far but can't figure out how to save with preserved recursive directories
for i in `find -name . "*.flv"`; do ffmpeg -i "$i" "${i%.*}.mp4"; done
maybe there was a better way but this ultimately worked for my purposes. i had to rename the files and directories to remove spaces and rework the find command. the xargs inclusion tried to account for spaces but it didnt work
so i removed spaces from directories with this:
for f in *; do mv "$f" `echo $f | tr ' ' '_'`; done
and removed spaces from filenames with this
find . -type f -name "* *.flv" -exec bash -c 'mv "$0" "${0// /_}"' {} \;
then this command recursively reencoded my flv files and saved in their directory
for i in `find . -name "*.flv" -print0| xargs -0`; do ffmpeg -i "$i" -c:v libx264 -f mp4 "${i%.*}.mp4"; done

Renaming the output file name from ffmpeg when using find and xargs

I am trying to rename the output files that are coming out of ffmpeg processes that I have spawned using find and xargs. I have a directory of files that I would like to convert to a certain format and all those files should ultimately be named .mov. So I am filtering out .mp4 & .mov files and then using xargs to pass it on to ffmpeg.
Suppose there are files such as abc.mp4, abcd.mp4, abcde.mp3, bcd.mov etc in the current working dir and then I am doing something like this to filter out just the mp4 and mov files and pass it on to ffmpeg for encoding:
find . -type f -name "*.mp4" -o -name "*.mov" | xargs -n 1 -p 5 -I '{}' ffmpeg -i '{}' ....... $PWD/{}"_h264.mov"
This will do what I want ffmpeg to do but it will name the files like
abc.mp4_h264.mov,abcd.mp4_h264.mov,bcd.mov_h264.mov
What I would ideally like to achieve is to name the output files as
abc_h264.mov,abcd_h264.mov,bcd_h264.mov
I am not getting any sparks in my brain on how to do that at the place where I specify the output files of ffmpeg. I know that I can call basename command using xargs like this
find . -type f -name "*.mp4" -o -name "*.mov" | xargs -n 1 basename | rev | cut -d . -f 2 | rev
and extract the filename without the extension but if i use that then how can I pass it on to ffmpeg as the input file because it would have lost the extension when it comes out of xargs and ffmpeg will throw an error.
Could anyone help me achieve this in one go where I can specify what 'basename' is doing but where I am specifying the output file name of ffmpeg please?
Thanks very much for any help that you can provide. Really appreciate the community.
Would you please try the following:
find . -type f -name "*.mp4" -o -name "*.mov" | xargs -I {} -n 1 -P 5 bash -c 'org="{}"; new="$PWD/${org%.*}_h264.mov"; ffmpeg -i "$org" {options} "$new"'
But it is not efficient for spawning bash command as a subprocess
multiple times. If you do not have a specific reason to use find
and xargs, it will be simpler to say:
for org in *.mp4 *.mov; do
new="$PWD/${org%.*}_h264.mov"
ffmpeg -i "$org" {options} "$new"
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 loop over directory tree with ffmpeg, wrong spaces? [duplicate]

This question already has answers here:
Iterate over a list of files with spaces
(12 answers)
How to loop through file names returned by find?
(17 answers)
Closed 5 years ago.
I am trying to interate over multiple video files that are grouped in directories, and ffmpeg returns errors about paths, it seems like paths are broken, end at first space. Can you point me too what is the problem here? Files and directories have spaces.
$ for f in $(find -type f -name *.mkv); do ffmpeg -n -i "$f" -c:v copy "~/Pobrane/$f" ; done
Loop splits paths by space and takes words as entries. How to fix this?
$ for f in $(find -type f -name *.mkv); do echo "$f"; done
./homeworks/2017-04-03
00-54-57
homework3b.mkv
./homeworks/2017-04-03
00-21-36
homework1.mkv
./homeworks/2017-04-03
Replacing the Loop
Use
find -type f -name '*.mkv' -exec ffmpeg -n -i {} -c:v copy ~/Pobrane/{} \;
-exec executes the following ffmpeg command for each of the found paths, replacing {} with the current path. The ; informs find that the ffmpeg command ends there.
Quote '*.mkv' in order to pass the literal string to find, which then searches for files ending with *.mkv. If you do not quote the string and have some mkv files laying around in your working directory, the shell will expand the unquoted *.mkv resulting in find -type f -name firstFile.mkv secondFile.mkv ... before starting find.
Do not quote ~. The unquoted ~ expands to your home directory (probably /home/yourname) but the quoted '~' is a directory/file with the literal name ~ .
Creating Parent Directories
How would I add mkdir -p before the ffmpeg call?
You could wrap the mkdir and ffmpeg in one function and execute the function:
myFunction() {
mkdir -p "$(dirname "$1")"
ffmpeg -n -i "$1" -c:v copy ~/Pobrane/"$1"
}
export -f myFunction
find -type f -name '*.mkv' -exec bash -c 'myFunction "$0"' {} \;
or use a loop:
find . -type f -iname "*.txt" -print0 | while IFS= read -r -d $'\0' file; do
mkdir -p "$(dirname "$file")"
ffmpeg -n -i "$file" -c:v copy ~/Pobrane/"$file"
done

xargs - multiple commands and rm at the end

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.

Resources