while loop - done < command instead of done < file
You don't need any loop here, find
can do it for you:
find . -name '*.avi' -exec mv {} . \;
You can also use process substitution.
while IFS= read -r f
do
mv -- "$f" .
done < <(find . -name '*.avi' )
In shells like bash
, compared to the pipe approach, it has the advantage of not running the loop in a subshell, so the variable assignments you would do in the loop for instance are not lost afterwards. In bash
(or zsh
), you'd rather do:
while IFS= read <&3 -rd '' f
do
mv -- "$f" .
done 3< <(find . -name '*.avi' -print0)
using NUL delimiters to be able to work with arbitrary file names, using fd 3 instead of 0 as mv
may prompt the user and would read the answer on stdin which would be the output of find
if you used 0.
Or using the find
command itself
find . -name '*.avi' -exec mv -t . {} +
Using +
means we pass many arguments at a time to mv
which saves having to run one mv
invocation per file. -t
is a GNU extension. With other mv
implementations, you can change it to:
find . -name '*.avi' -exec sh -c 'exec mv "$@" .' sh {} +
proper way to do this is pipe
find . -name '*.avi' |
while read f
do
mv "$f" .
done
the result from first command "find . -name *.avi
" will feed the second.
This is call a pipe (from the symbol |
).
You can think of pipe as a temporary file like
find . -name '*.avi' > file1.tmp
while read f
do
mv "$f" .
done < file1.tmp
rm file1.tmp
As pointed out filename with space or newline are likely to cause error.
If the only purpose is to move file, use @Terdon's commands.
Also you must quote *.avi
or it will break on second run.
find: paths must precede expression: `foo.avi'
find: possible unquoted pattern after predicate `-name'?