You are not logged in.
I have written a few little bash scripts that simplify tasks, one of them converts my FLAC files to MP3 files. It's just a simple front-end to ffmpeg.
What I cannot figure out is, how can I pass multiple files to the script? I would like to be able to do...
flac2mp3 *.flac
And have the script take all flac files as arguments.
I understand that the shell will expand the wildcard before it is "send" to my script, but the problem is, that my filenames have spaces, and I just cannot figure out how to get each file separately. I end up with either split words or all files as one argument. Do I have to resort to another language? Python would do, but I feel like there must be a way to do this in bash?
Offline
In the absence of your current script, something like this will work.
$ cat loop-filenames.sh
#!/bin/bash
for arg in "$@"; do
echo "Convert file '$arg'"
done
$ ./loop-filenames.sh example.mp3 "foo bar.mp3"
Convert file 'example.mp3'
Convert file 'foo bar.mp3'
$
Are you familiar with our Forum Rules, and How To Ask Questions The Smart Way?
BlueHackers // fscanary // resticctl
Offline
Sorry, of course I should have provided a code example.
I believe the problem in my script is, that I am using a function to convert the files, and am trying to pass the "$@" as a variable to the function, which somehow doesn't work. Here is a simplified example of what I am trying to do:
#!/usr/bin/env bash
# Simplified for demonstration purposes, trying to make `program *.flac` work
#
# Converts FLAC file(s) `$1` to MP3 files and saves these to directory `$2`
# $1 (str) - file path or pattern to FLAC files
# $2 (str) (optional) - path to output directory, default is current directory
function convertFlacs() {
cd "${2:-.}"
for f in "${1}"; do
#ffmpeg -i "${f}" -b:a 256k "${f/%flac/mp3}"
echo "${f}"
done
cd "${OLDPWD}"
}
# Get options
while getopts o: options; do
case "${options}" in
o) output_dir="${OPTARG}";;
esac
done
shift $(($OPTIND - 1))
files="$@"
# Run functions
convertFlacs "${files}" "${output_dir}"
If I don't use a function, it would work with the example you provided. But since I have a few other functions in the script, I would like to keep things separated by using functions...
Is there a solution to do this?
Offline
for file in *.flac; do
ffmpeg -i "$file" -ab 320k -map_metadata 0 -id3v2_version 3 "${file%.flac}.mp3"
done
(( $? == 0 )) && rm *.flac
Offline
for file in *.flac; do ffmpeg -i "$file" -ab 320k -map_metadata 0 -id3v2_version 3 "${file%.flac}.mp3" done (( $? == 0 )) && rm *.flac
I do understand that part. But in my script I want to offer a few more options, like a different output directory, sample rate, etc.
Anyway, I believe I have now figured out how to pass files to the script, and use them in a function. This sample snippet works as far as I can see:
#!/usr/bin/env bash
# Simplified for demonstration purposes, trying to make `program *.flac` work
# Default variables
output_dir="${PWD}"
# Converts FLAC file(s) `$1` to MP3 files and saves these to `$output_dir`
# $1 (str) - file path or pattern to FLAC files
function convertFlacs() {
cd "${output_dir}"
files=("$@")
for f in "${files[@]}"; do
#ffmpeg -i "${f}" -b:a 256k "${f/%flac/mp3}"
echo "${f}"
done
cd "${OLDPWD}"
}
# Get options
while getopts o: options; do
case "${options}" in
o) output_dir="${OPTARG}";;
esac
done
shift $(($OPTIND - 1))
files=("$@")
# Run functions
convertFlacs "${files[@]}"
Offline
OK, I have my script working fine now. I am sure it could be polished some more, any comments are welcome.
Here the full script:
#!/usr/bin/env bash
# Simple script that uses ffmpeg to convert flac files to MP3 files.
# @author: DrTebi@gmail.com
# @since: 2014-11-30
# @version: 0.4
# @license: GPL
program="${0##*/}"
program_helpers=('ffmpeg')
sample_rates=(64 128 160 192 256 320)
sample_rate_default=256
output_dir="${PWD}"
#
# Echos arguments as colored text with no new-line
# $1 (int) - color code
# $2..n (str) - text to colorize
function echoColor() {
color="$1"
shift
text="$@"
echo -n "[0;${color}m${text}[1;0m"
}
#
# Shows basic program usage
function showUsage() {
echoColor 33 "Usage: ${program} -h | [ -r <sample-rate> ] [-o <output-directory> ] <flac-file(s)>"
echo
}
#
# Shows program help
function showHelp() {
cat << EOF
${program} converts FLAC files to MP3 files.
If no <output-directory> is not specified, MP3 files will be saved to the
current directory.
EOF
showUsage
cat << EOF
Options are:
-h Display this help message
-r <sample-rate> Use specified sample rate <sample-rate> instead of default ${sample_rate_default}.
Can be one of 64, 128, 160, 192, 256, or 320
-o <output-directory> The directory where MP3 files will be saved to
Required arguments:
<flac-file(s)> The path to one or more FLAC files, e.g. "Blues/*.flac"
EOF
}
#
# Checks whether programs defined in `$program_helpers` exist
function checkPrograms() {
local not_found=()
for x in "${program_helpers[@]}"; do
type "${x}" > '/dev/null' 2>&1 || not_found+=("${x}")
done
if [[ ${#not_found[@]} != 0 ]]; then
echoColor 31 "Error: ${program} requires the following programs: ${not_found[@]}"
echo
exit 1
fi
}
#
# Returns true if array `$2` contains `$1`
# $1 (str|int) - the element value to search for
# $2 (array) - the array to be searched
function in_array() {
local needle=$1
shift
local array=($@)
for el in "${array[@]}"; do
if [[ "${el}" == "${needle}" ]]; then
return 0;
fi
done
return 1
}
#
# Converts FLAC files `$files` to MP3 files and saves them to `$output_dir`
function convertFlacs() {
for f in "${files[@]}"; do
mp3_filename="${f/%flac/mp3}"
ffmpeg -vsync 2 -i "${f}" -b:a "${sample_rate_default}"k "${output_dir}"/"${mp3_filename##*/}"
done
}
#
# Get arguments and files
while getopts r:o:h options; do
case "${options}" in
r) sample_rate_default="${OPTARG}";;
o) output_dir="${OPTARG}";;
h) showHelp && exit 0;;
\?) showUsage && exit 1;;
esac
done
if ! in_array "${sample_rate_default}" "${sample_rates[@]}"; then
echoColor 31 "Error: Invalid sample rate: ${sample_rate_default}"
echo
exit 1
fi
shift $(($OPTIND - 1))
files=("$@")
if [[ ! "${files}" ]]; then
echoColor 31 "Error: No files specified"
echo
exit 1
fi
# Run functions
checkPrograms
convertFlacs
Thanks for everyones help!
Offline
I have not really tested yet, but shouldn't this be
echo -ne "\033[${color}m${text}\e[0m"
? Otherwise I get no color output;)
Offline
I have not really tested yet, but shouldn't this be
echo -ne "\033[${color}m${text}\e[0m"
? Otherwise I get no color output;)
You are right. The escape characters appear to got lost with copy and paste, or maybe with the BB code.
In my editor it actually looks like this, which I think is just another way of writing \033 ?
echo -n "^[[0;${color}m${text}^[[1;0m"
Offline
...
In my editor it actually looks like this, which I think is just another way of writing \033 ?
echo -n "^[[0;${color}m${text}^[[1;0m"
Could be but it isn't usable code in bash/zsh xterm/urxvt..;)
Offline
drtebi wrote:...
In my editor it actually looks like this, which I think is just another way of writing \033 ?
echo -n "^[[0;${color}m${text}^[[1;0m"
Could be but it isn't usable code in bash/zsh xterm/urxvt..;)
Hmmm... works fine in my terminal with bash. But \033 is probably safer.
Offline
ffmpeg -vsync 2 -i "${f}" -b:a "${sample_rate_default}"k "${output_dir}"/"${mp3_filename##*/}"
-b:a is the bitrate, but you are calling it sample rate (-ar option) which is something completely different.
No need for "-vsync 2" (and don't use it as an input option). Use "-c:v copy" output option instead to deal with cover/album image.
Offline
Thanks for the hint. So the "-c:v copy" option is only useful to ensure the cover image is copied? I looked up the documentation, but don't quite understand this option.
Regarding "-b:a", you are referring to my choice of variable name I suppose? I should call it bitrate, makes more sense of course.
Offline
Thanks for the hint. So the "-c:v copy" option is only useful to ensure the cover image is copied? I looked up the documentation, but don't quite understand this option.
"-c:v copy" will just re-mux (stream copy) the cover image instead of additionally re-encoding it. If the input has no cover image then this option will be ignored. Using "-c:v copy" should allow you to avoid the warning:
Frame rate very high for a muxer not efficiently supporting it.
Please consider specifying a lower framerate, a different muxer or -vsync 2
Regarding "-b:a", you are referring to my choice of variable name I suppose? I should call it bitrate, makes more sense of course.
Yes.
Offline
OK, thanks for your input, works great, just tested. Here is the updated script:
#!/usr/bin/env bash
# Simple script that uses ffmpeg to convert flac files to MP3 files.
# @author: DrTebi@gmail.com
# @since: 2014-11-30
# @version: 0.4.1
# @license: GPL
program="${0##*/}"
program_helpers=('ffmpeg')
bit_rates=(64 128 160 192 256 320)
bit_rate_default=256
output_dir="${PWD}"
#
# Echos arguments as colored text with no new-line
# $1 (int) - color code
# $2..n (str) - text to colorize
function echoColor() {
color="$1"
shift
text="$@"
echo -e -n "\033[0;${color}m${text}\033[1;0m"
}
#
# Shows basic program usage
function showUsage() {
echoColor 33 "Usage: ${program} -h | [ -r <bit-rate> ] [-o <output-directory> ] <flac-file(s)>"
echo
}
#
# Shows program help
function showHelp() {
cat << EOF
${program} converts FLAC files to MP3 files.
If no <output-directory> is not specified, MP3 files will be saved to the
current directory.
EOF
showUsage
cat << EOF
Options are:
-h Display this help message
-r <bit-rate> Use specified bit rate <bit-rate> instead of default ${bit_rate_default}.
Can be one of 64, 128, 160, 192, 256, or 320
-o <output-directory> The directory where MP3 files will be saved to
Required arguments:
<flac-file(s)> The path to one or more FLAC files, e.g. "Blues/*.flac"
EOF
}
#
# Checks whether programs defined in `$program_helpers` exist
function checkPrograms() {
local not_found=()
for x in "${program_helpers[@]}"; do
type "${x}" > '/dev/null' 2>&1 || not_found+=("${x}")
done
if [[ ${#not_found[@]} != 0 ]]; then
echoColor 31 "Error: ${program} requires the following programs: ${not_found[@]}"
echo
exit 1
fi
}
#
# Returns true if array `$2` contains `$1`
# $1 (str|int) - the element value to search for
# $2 (array) - the array to be searched
function in_array() {
local needle=$1
shift
local array=($@)
for el in "${array[@]}"; do
if [[ "${el}" == "${needle}" ]]; then
return 0;
fi
done
return 1
}
#
# Converts FLAC files `$files` to MP3 files and saves them to `$output_dir`
function convertFlacs() {
for f in "${files[@]}"; do
mp3_filename="${f/%flac/mp3}"
ffmpeg -i "${f}" -c:v copy -b:a "${bit_rate_default}"k "${output_dir}"/"${mp3_filename##*/}"
done
}
#
# Get arguments and files
while getopts r:o:h options; do
case "${options}" in
r) bit_rate_default="${OPTARG}";;
o) output_dir="${OPTARG}";;
h) showHelp && exit 0;;
\?) showUsage && exit 1;;
esac
done
if ! in_array "${bit_rate_default}" "${bit_rates[@]}"; then
echoColor 31 "Error: Invalid bit rate: ${bit_rate_default}"
echo
exit 1
fi
shift $(($OPTIND - 1))
files=("$@")
if [[ ! "${files}" ]]; then
echoColor 31 "Error: No files specified"
echo
exit 1
fi
# Run functions
checkPrograms
convertFlacs
I am sure it could be improved further, but for now it's pretty neat and simple already.
Last edited by drtebi (2020-03-05 05:02:36)
Offline