The command:
find /root/* -mindepth 1 -maxdepth 1 -type f
finds all files (-type f
) that are at depth 1 (-mindepth 1 -maxdepth 1
) inside the sub-directories of /root
. The -mindepth
argument forces find
to skip the files from /root
(they are at depth 0
) that would otherwise be found.
Run the command above and verify that it produces the list of files you want to move and nothing else. You can make it more strict to make sure it doesn't return files you don't want to move. For example, if you know all the subdirectories of /root
that you want to process start with 12
then you can use find /root/12* ...
.
When you are sure you have the command that produces the correct list of files, embed it into a for
command as follows:
IFS=$'\n'
for i in $(find /root/* -mindepth 1 -maxdepth 1 -type f); do
mv "$i" "${i%/*}/medium/"
done
Just to make sure it doesn't break anything, put echo
in front of mv
and run it to see what it will do. If everything is ok then remove the echo
and enjoy.
Remark
Make sure you run the above command using bash
. The $()
construct is bash-specific; other shells may or may not understand it or they may interpret it in a different way. A more-portable way to express the same behaviour is by using backticks (``
) instead of $()
.
How it works
The command substitution operator ($()
or ``
) runs the command it contains and captures its output. The output of the find
command is the list of files to move.
The string produced by $()
is then iterated by for
; on each iteration, the environment variable i
is set with one line of the output of find
. Each line of the output of find
contains the path of one file we want to move.
Setting the IFS
environment variable to $'\n'
before the for
block ensures the output of find
is split on newlines only. The default value of IFS
(the "Internal Field Separator") is <space><tab><newline>
and it makes the for
command incorrectly split the output of find
if it finds files that have spaces in their names.
If you are absolutely sure the files you want to move do not have spaces in their names then you can remove the IFS=$'\n'
line.
Next, on each iteration a mv
command line is built and executed. The ${i%/*}
parameter expansion removes from the value of i
the shortest right piece (%
) that matches the /*
pattern. The removed part is the filename and the slash that precedes it in the full file path.
${i%/*}/medium/
basically replaces the file name with medium/
. Another possible way to do the same thing is:
mv "$i" "$(dirname $i)/medium/"
It uses the command dirname
to extract from $i
only the directory (remove the file name).
The first way (${i%/*}
) is preferred because it is faster.