Я уже использовал StackExchange и скрывался в SuperUser, но впервые публикую сообщения. Вот что я пытаюсь сделать. (Псевдо)

For Each DIR containing $file *.Item
    Mkdir ./dir1/dir2
    Mv $file ./dir1/dir2/$file

В основном у меня есть структура папок, полная файлов типа.Элемент, в каждом каталоге, содержащем файл типа.Пункт Я хочу создать 2 новых подкаталога, назовите их dir1/dir2/, а затем я хочу переместить все файлы типа.Предмет в эти подкаталоги.

До сих пор мне повезло, что я выполнил некоторые из них, но не все сразу. Я прошу прощения, если на это уже был дан ответ.

1 ответ1

0

Основная проблема здесь состоит в том, чтобы избежать перемещения файлов более одного раза. Наивные циклы рискуют найти 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"

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .