Bash while loop stops for no reason? [duplicate] - bash

This question already has answers here:
While loop stops reading after the first line in Bash
(5 answers)
Closed 3 years ago.
I run the following bash script to rotate my mobile phone vids
while read filename ; do
nf=$(echo $filename |rev | cut -f1 -d '/'|cut -f2- -d '.' |rev)
echo $nf
rm -f ffmpeg2pass-0.log
rm -f rotate/tmp.avi
ffmpeg -i $filename -c:v libxvid -pass 1 -c:a libmp3lame -qscale:a 4 -vf "transpose=2,transpose=2" "rotate/tmp.avi"
ffmpeg -i $filename -c:v libxvid -pass 2 -c:a libmp3lame -qscale:a 4 -vf "transpose=2,transpose=2" "rotate/$nf.avi"
done <rotatelist_2
I know there are better ways to do this; I budged this together but I'm figuring out how to do the videos right so the rest don't need to look nice ;-))
However after the first run, the loop unexpectedly ends with no error message. I run similar loops for other things which work pretty well.
The echo is not called again so I guess there's something wrong with the loop itself. The linebreak in the list is a 0x0A, so it should be ok.

Use
</dev/null ffmpeg ...
</dev/null ffmpeg ...
or
ffmpeg ... </dev/null
ffmpeg ... </dev/null
to prevent ffmpeg reading from rotatelist_2 via stdin.

Related

Problem with escaped quotes in variable (I think) [duplicate]

This question already has an answer here:
Pass dynamically generated parameters to command inside script
(1 answer)
Closed 11 months ago.
I am trying to write a parameter driven routine to extract parts of audio files using ffmpeg.
Because the routine is parameter driven I end up with a number of options in variables (a technique I have used successfully before in simpler examples) and for some reason this time it isn't working. having stared at it and tried various experiments for hours I give up and hope the helpful experts can sort me out
This is a simplified version with the variables set directly
...
#!/bin/bash
a="a b c.mp3"
b="out-$a"
trackstring="-metadata track=\"07/93\""
echo "trackstring=$trackstring"
titlestring="-metadata title=\"$a\""
echo "titlestring=$titlestring"
startpoint="-ss 0"
echo "startpoint=$startpoint"
endpoint="-to 300"
echo "endpoint=$endpoint"
coverstring="-c:v copy"
echo "coverstring=$coverstring"
audiostring="-c:a libmp3lame -ab 32k -ac 1"
echo "audiostring=$audiostring"
echo "ffmpeg $startpoint $endpoint -i \"$a\" -hide_banner -loglevel warning $coverstring $audiostring $titlestring $trackstring \"$b\""
ffmpeg $startpoint $endpoint -i "$a" -hide_banner -loglevel warning $coverstring $audiostring $titlestring $trackstring "$b"
...
The resulting output from my script looks like this:
trackstring=-metadata track="07/93"
titlestring=-metadata title="a b c.mp3"
startpoint=-ss 0
endpoint=-to 300
coverstring=-c:v copy
audiostring=-c:a libmp3lame -ab 32k -ac 1
ffmpeg -ss 0 -to 300 -i "a b c.mp3" -hide_banner -loglevel warning -c:v copy -c:a libmp3lame -ab 32k -ac 1 -metadata title="a b c.mp3" -metadata track="07/93" "out-a b c.mp3"
Which gives me exactly what I am expecting and I think all valid BUT....
Then ffmpeg gives me:
[mp3 # 0x55ae679e4640] Estimating duration from bitrate, this may be inaccurate
[NULL # 0x55ae679ea0c0] Unable to find a suitable output format for 'b'
b: Invalid argument
A bad one!
But interestingly putting the whole command into a string and the using an explicit sub-shell works exactly as expected: So starting from the last of the assignments in the original post
...
audiostring="-c:a libmp3lame -ab 32k -ac 1"
echo "audiostring=$audiostring"
cmd="ffmpeg $startpoint $endpoint -i \"$1\" -hide_banner -loglevel warning $coverstring $audiostring $titlestring $trackstring \"$2\""
echo "$cmd"
bash -c "$cmd"
Frankly, although I now have "working code" I think I am more confused than before.
The output is unchanged (except no error from ffmpeg) and the file(s) are generated exactly as expected

ffmpeg / batch addition of watermarks for videos [duplicate]

This question already has answers here:
How do you convert an entire directory with ffmpeg?
(34 answers)
Closed 1 year ago.
I used with script to add dynamic watermark to one video, how to adapt it to convert multiple videos?
ffmpeg -i test.mp4 -i logo.png -filter_complex "[1]colorchannelmixer=aa=0.6,scale=iw*0.7:-1[a];[0][a]overlay=x='if(lt(mod(t\,16)\,8)\,W-w-W*5/100\,W*5/100)':y='if(lt(mod(t+4\,16)\,8)\,H-h-H*2.5/100\,H*2.5/100)'" -c:v libx264 -an out.mp4
You just need to iterate the video files:
source_folder=$1
target_folder=$2
mkdir -p $target_folder
echo "procesing..."
for file in $source_folder/*.mp4 $source_folder/**/*.mp4 ; do
if [[ -f $file ]]; then
filename=$(basename -- "$file")
echo "source video:"$file "new :"$target_folder/$filename
ffmpeg -i "$file" -i logo.png -filter_complex "[1]colorchannelmixer=aa=0.6,scale=iw*0.7:-1[a];[0][a]overlay=x='if(lt(mod(t\,16)\,8)\,W-w-W*5/100\,W*5/100)':y='if(lt(mod(t+4\,16)\,8)\,H-h-H*2.5/100\,H*2.5/100)'" -c:v libx264 -an "$target_folder/$filename"
fi
done;
echo ""
echo "result: $target_folder"
find $target_folder | sort
Example:
bash script.sh /input /foo/bar/output

/usr/local/bin/ffmpeg: cannot execute binary file

Want to convert my m4a files into mp3 files using a script. It would save some time... I have over 100 GB of music files.
OS: OSX10.14 / Terminal vs Bash script
I can run ffmpeg -v 5 -y -i musicFile.m4a -acodec libmp3lame -ac 2 -b:a 320k musicFile.mp3 from the terminal. It converts the file and I can see and play the file from itunes.
When I run the same from a bash script it fails to convert.
ffmpeg -v 5 -y -i $ENTRY_FILE -acodec libmp3lame -ac 2 -b:a 320k $MP3NAME
My ipod nano just died and I got a new mp3 player. Now I need to convert my itunes files from AAC format to MP3.
ffmpeg is an established video and music file converter.
When I run it from the bash script I tried a few things.
I added ./ in front of the file, that failed because it was installed under /usr/local/bin and not under the same directory.
I also tried sh ffmpeg... and that gave me the cannot execute a binary file.
#!/usr/bin/env bash
# convert m4a file to mp3
set -e
file_convert() {
ENTRY_FILE=$(printf %q "${entry}")
FILE_NAME=$(printf %q "$(basename "${entry}")")
DIR=$(printf %q "$(dirname "${entry}")")
NAME="${FILE_NAME%.*}"
EXT="${FILE_NAME##*.}"
MP3NAME="${DIR}/${NAME}.mp3"
printf "%*s%s\n" $((indent+2)) '' "$ENTRY_FILE"
printf "%*s\tNew File :\t%s\n" $((indent+2)) '' "$MP3NAME"
if [ $EXT == "m4a" ]
then
printf "%*s\tConverting: \t%s\n" $((index+2)) '' "$ENTRY_FILE"
ffmpeg -v 5 -y -i $ENTRY_FILE -acodec libmp3lame -ac 2 -b:a 320k $MP3NAME
fi
}
walk() {
local indent="${2:-0}"
printf "\n%*s%s\n\n" "$indent" '' "$1"
# If the entry is a file convert it
for entry in "$1"/*; do [[ -f "$entry" ]] && file_convert; done
# If the entry is a directory recurse
for entry in "$1"/*; do [[ -d "$entry" ]] && walk "$entry" $((indent+2)); done
}
# If the path is empty use the current, otherwise convert relative to absolute; Exec walk()
[[ -z "${1}" ]] && ABS_PATH="${PWD}" || pushd "${1}" && ABS_PATH="${PWD}"
walk "${ABS_PATH}"
popd
echo
I expect >./aacToMp3.sh ./music to traverses the music directory and convert each m4a file to .mp3.
It is walking the file system and printing out correct files, with the spaces escaped. When it hits the ffmpeg line it halts. I put the set -e at the top of the file to force it to fail if the command fails. Without the set -e it happily walks all the music files and prints them to the stdout.
If you have lots of files to process and a decent multi-core CPU and fast disk, I would recommend GNU Parallel which you can install with homebrew:
brew install parallel
Then make a copy of a few files in a test directory and try:
parallel --dry-run ffmpeg -v 5 -y -i {} -acodec libmp3lame -ac 2 -b:a 320k {.}.mp3 ::: *.m4a
If that looks good, replace --dry-run with --progress.
If that looks good, you can (make a backup first) and do the whole lot:
find path/to/music -name "*.m4a" -print0 | parallel -0 --progress ffmpeg -v 5 -y -i {} -acodec libmp3lame -ac 2 -b:a 320k {.}.mp3
Thanks for all the input.
After a while I ended up writing all the ffmpeg lines to a script file. Glad I did. I was able to quickly scan the file and see some errors and fix them.
This is what I came up with.
Basically I am writing straight text to stdout and directing it to a file. Which I converted to a shell script to convert each file. One at a time. Ran the generated script overnight.
file_convert() {
ENTRY_FILE=$(printf %q "${entry}")
FILE_NAME=$(printf %q "$(basename "${entry}")")
DIR=$(printf %q "$(dirname "${entry}")")
NAME="${FILE_NAME%.*}"
EXT="${FILE_NAME##*.}"
MP3NAME="${DIR}/${NAME}.mp3"
if [ $EXT == "m4a" ]
then
printf 'echo "Converting %s ..."\n' "$FILE_NAME"
printf 'ffmpeg -v 5 -y -i %s -acodec libmp3lame -ac 2 -b:a 320k %s\n\n' "$ENTRY_FILE" "$MP3NAME"
fi
}
The output looked like.
#!/usr/bin/env bash
echo "Converting 03\ It\'s\ Not\ My\ Time.m4a ..."
ffmpeg -v 5 -y -i /Users/arthuranderson/Documents/work/projects/mp3Convert/music/3\ Doors\ Down/3\ Doors\ Down\ \(Bonus\ Track\ Version\)/03\ It\'s\ Not\ My\ Time.m4a -acodec libmp3lame -ac 2 -b:a 320k /Users/arthuranderson/Documents/work/projects/mp3Convert/music/3\ Doors\ Down/3\ Doors\ Down\ \(Bonus\ Track\ Version\)/03\ It\'s\ Not\ My\ Time.mp3
echo "Converting 01\ Down.m4a ..."
ffmpeg -v 5 -y -i /Users/arthuranderson/Documents/work/projects/mp3Convert/music/311/Greatest\ Hits\ \'93-\'03/01\ Down.m4a -acodec libmp3lame -ac 2 -b:a 320k /Users/arthuranderson/Documents/work/projects/mp3Convert/music/311/Greatest\ Hits\ \'93-\'03/01\ Down.mp3
Mark I will try parallel when I purchase some more music next time.
Thanks everyone!

Execute "ffmpeg" command in a loop [duplicate]

This question already has answers here:
While loop stops reading after the first line in Bash
(5 answers)
Closed 2 years ago.
I have three .wav files in my folder and I want to convert them into .mp3 with ffmpeg.
I wrote this bash script, but when I execute it, only the first one is converted to mp3.
What should I do to make script keep going through my files?
This is the script:
#!/bin/bash
find . -name '*.wav' | while read f; do
ffmpeg -i "$f" -ab 320k -ac 2 "${f%.*}.mp3"
done
Use the -nostdin flag to disable interactive mode,
ffmpeg -nostdin -i "$f" -ab 320k -ac 2 "${f%.*}.mp3"
or have ffmpeg read its input from the controlling terminal instead of stdin.
ffmpeg -i "$f" -ab 320k -ac 2 "${f%.*}.mp3" </dev/tty
See the -stdin/-nostdin flags in the ffmpeg documentation
If you do need find (for looking in subdirectories or performing more advanced filtering), try this:
find ./ -name "*.wav" -exec sh -c 'ffmpeg -i "$1" -ab 320k -ac 2 "$(basename "$1" wav).mp3"' _ {} \;
Piping the output of find to the while loop has two drawbacks:
It fails in the (probably rare) situation where a matched filename contains a newline character.
ffmpeg, for some reason unknown to me, will read from standard input, which interferes with the read command. This is easy to fix, by simply redirecting standard input from /dev/null, i.e. find ... | while read f; do ffmpeg ... < /dev/null; done.
In any case, don't store commands in variable names and evaluate them using eval. It's dangerous and a bad habit to get into. Use a shell function if you really need to factor out the actual command line.
No reason for find, just use bash wildcard globbing
#!/bin/bash
for name in *.wav; do
ffmpeg -i "$name" -ab 320k -ac 2 "${name%.*}.mp3"
done

Run a function from while read line

I'm trying to run a function from while read line, the function contains ffmpeg commands to marge two files. but for some reason it's running the first $line and than breaks from loop.
"$filesList" contains three lines. I'm not sure what's wrong, but i can confirm with echo "$OFILE" that opener function runs three times if I comment out the ffmpeg commands, and only once with ffmpeg commands.
opener(){
OFILE="$1"
echo "$OFILE"
ffmpeg -i $opener_path -c copy -bsf:v h264_mp4toannexb -f mpegts intermediate1.ts
ffmpeg -i $OFILE -c copy -bsf:v h264_mp4toannexb -f mpegts intermediate2.ts
ffmpeg -i "concat:intermediate1.ts|intermediate2.ts" -c copy -bsf:a aac_adtstoasc merge_$OFILE
mv merge_$OFILE $OFILE
rm intermediate1.ts intermediate2.ts
}
while read line; do
if [ -e "$line" ]; then
opener "$line"
fi
done <<< "$filesList"
It appears one of the ffmpeg commands is reading from standard input, which consumes the rest of the contents of $filesList before the next call to read. I'm not familiar with ffmpeg, but two possibilities:
Does -i require an argument? Your posted code doesn't set the value of opener_path, so its unquoted expansion would produce an empty string that is discarded by the shell.
How is concat:intermediate1.ts|intermediate2.ts interpreted by ffmpeg? Given the call to rm, it seems to produce a pair of files from an unknown source.

Resources