You are using the wrong replacement string with parallel
; you'll also need -q
to pass quoted arguments and I'm not sure what the trailing \;
does...
Example:
dirname_with_spaces="/home/don/my dir with spaces/"
(note the trailing /
in the path assigned to dirname_with_spaces
) and some mp3
file names with spaces under a test
directory, right under cwd
:
./test/Commercial DEMO - 09.mp3
./test/Commercial DEMO - 11.mp3
./test/Handel Royal Fireworks - 07.mp3
./test/Jazz Rag Ensemble - 10.mp3
./test/Mouret - Rondeau.mp3
using
find . -type f -iname \*.mp3 | parallel -q ffmpeg -i {} -acodec \
libmp3lame -ab 128k "$dirname_with_spaces"{/.}-128k.mp3
produces the following files:
/home/don/my dir with spaces/Commercial DEMO - 11-128k.mp3
/home/don/my dir with spaces/Commercial DEMO - 09-128k.mp3
/home/don/my dir with spaces/Handel Royal Fireworks - 07-128k.mp3
/home/don/my dir with spaces/Jazz Rag Ensemble - 10-128k.mp3
/home/don/my dir with spaces/Mouret - Rondeau-128k.mp3
Note the command line q
uoting (parallel -q
) and the usage of:
{}
Input line.
which means the path from find
output e.g. ./test/Mouret - Rondeau.mp3
and
{/.}
Basename of input line without extension.
which expands to Mouret - Rondeau
and then "$dirname_with_spaces"{/.}
expands to /home/don/my dir with spaces/Mouret - Rondeau
The latter is quite different from the {.}
used in your command
{.}
Input line without extension.
which would expand to ./test/Mouret - Rondeau
and then "$dirname_with_spaces"{.}
would expand to /home/don/my dir with spaces/./test/Mouret - Rondeau
. Obviously, this will error out as there is no /./test/
under /home/don/my dir with spaces
.
Use arrays.
If you don't need to handle the possibility of newlines in your filenames, then you could get away with
mapfile -t ABC_FILES < <(find -L some/dir -name \*.abc | sort)
mapfile -t XYZ_FILES < <(find -L other/dir -name \*.xyz | sort)
then
./program --abc-files "${ABC_FILES[@]}" --xyz-files "${XYZ_FILES[@]}"
If you do need to handle newlines within filenames, and have bash >= 4.4, you can use -print0
and -d ''
to null-terminate the names during array construction:
mapfile -td '' ABC_FILES < <(find -L some/dir -name \*.abc -print0 | sort -z)
(and similarly for the XYZ_FILES
). If you don't have the newer bash, then you could use a null-terminated read loop to append filenames to the arrays e.g.
ABC_FILES=()
while IFS= read -rd '' f; do ABC_FILES+=( "$f" ); done < <(find -L some/dir -name \*.abc -print0 | sort -z)
Best Answer
parallel
runs a shell (which exact one depending on the context in which it is called, generally, when called from a shell, it's that same shell) to parse the concatenation of the arguments.So:
is the same as
parallel
will call:Where
<something>
is the arguments (hopefully) properly quoted for that shell. For instance, if that shell isbash
, it will runHere, you want:
Or
Where
parallel
will quote the arguments (in the correct (hopefully) syntax for the shell) before concatenating.To avoid calling a shell in the first place, you could use GNU
xargs
instead:That won't invoke a shell (nor any of the many commands ran by
parallel
upon initialisation), but you won't benefit from any of the extra features ofparallel
, like output reordering with-k
.You may find other approaches at Background execution in parallel