Основная проблема здесь состоит в том, чтобы избежать перемещения файлов более одного раза. Наивные циклы рискуют найти dir/x.Item
, перемещая его в dir/dir1/dir2/x.Item
, а затем найти dir/dir1/dir2/x.Item
и перемещение его в dir/dir1/dir2/dir1/dir2/x.Item
, а потом ...
Хотя я не смог заставить Gnu find
это за пару минут тестирования, я отмечаю, что Posix говорит: «Если файл удален или добавлен в поисковую иерархию каталогов, не find
включает ли этот файл поиск в этот файл или нет в его поиске. " Так что нужно защищаться.
Поэтому самое простое безопасное решение - сначала создать список файлов, а затем обработать список.
Если в имени файла .Item
нет символа новой строки, тогда легко использовать find
для построения списка. Предполагая, что файлов не слишком много, это можно сделать непосредственно в массиве bash:
mapfile -t files < <(find . -name '*.Item')
Тогда это просто для обработки файлов:
for f in "${files[@]}"; do
# make the target directory if it doesn't yet exist (-p)
mkdir -p "$(dirname "$f")"/dir1/dir2
# move the file
mv "$f" "$(dirname "$f")/dir1/dir2
done
Если файлов много, мы должны быть более эффективными и менее требовательными к памяти. Мы можем создать список каталогов вместо списка файлов (sort -u
устраняет дубликаты):
mapfile -t dirs < <(find . -name '*.Item' -printf %h | sort -u)
Теперь мы можем обработать каталоги:
for d in "${dirs[@]}"; do
mkdir -p "$d/dir1/dir2"
mv "$d/*.Item" "$d/dir1/dir2"
done
Наконец, если бы было много каталогов, мы могли бы использовать временный файл вместо массива:
tmp=$(mktemp)
find . -name '*.Item' -printf %h | sort -u > "$tmp"
while IFS= read -r dir; do
mkdir -p "$d/dir1/dir2"
mv "$d/*.Item" "$d/dir1/dir2"
done < "$tmp"
rm "$tmp"