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'?

Tags:

Bash