Родительский дочерний каталог имеет то же имя, переместить файлы в родительский каталог

Родительский дочерний каталог имеет то же имя, переместить файлы в родительский каталог

Мне нужен способ поиска в каталогах дочерних каталогов с тем же именем, а затем перемещения всех файлов из дочернего каталога в родительский. Таким образом, /recup-dir1/recup-dir1/files to /recup-dir1/files. дочерние каталоги можно оставить пустыми, потому что я могу использовать что-то вроде find . -type -d -empty -delete удаления всех пустых каталогов

Проблема в том, что я понятия не имею, в каких каталогах есть дочерние каталоги с таким же именем, а в каких — нет.

В псевдокоде мне нужно что-то вроде этого.

While more directories are unchecked
get name-x of  next dir
   enter dir  
   If name-x/name-x exist
   move all files in name-x/name-x to name-x
   mark dir as done
next 

Лучшее, что я предлагаю, — это создать небольшой скрипт на Python, чтобы составить список всех каталогов, имеющих дочерние элементы с таким же именем, и пропустить этот список через команду вроде find something something -exec mv

Может быть, это можно сделать с помощью скриптов bash или есть другое решение. Например, какая-то команда rsync, однако, поскольку я создал этот беспорядок, вероятно, с помощью rsync, я не думаю, что это будет решением.

Редактировать: вот фактическая часть вывода дерева: Каталоги верхнего уровня находятся внутри /mnt/external-disk/tst-backup. На более низких уровнях нет подкаталогов.

│   └── recup_dir.1
├── recup_dir.10
│   └── recup_dir.10
├── recup_dir.100
│   └── recup_dir.100
├── recup_dir.102
│   └── recup_dir.102
└── recup_dir.1020
    └── recup_dir.1020

решение1

С помощью zshвы можете сделать:

for dir in **/*(NDodoN/e['[[ $REPLY:t = $REPLY:h:t ]]']); do
  contents=($dir/*(NDoN))
  (( $#contents == 0 )) ||
    mv -- $contents $dir:h/ &&
    rmdir -- $dir
done

Где:

  • **/*(qualifiers)рекурсивная подстановка с квалификаторами подстановки
  • N: nullglob: не жалуйтесь, если нет совпадений
  • D: dotglob: включить скрытые файлы
  • od: сначала сортировка по глубине (сначала листья, потом ветки, на которых они находятся).
  • oN: в противном случае не беспокойтесь об упорядочивании списка файлов.
  • /: ограничить файлами типа каталог.
  • e['expression']: ограничиться файлами, для которых expressionкод возвращает значение true (внутри которых хранится текущий путь к файлу $REPLY).
  • $REPLY:t: хвост (базовое имя) файла
  • $REPLY:h:t: хвост заголовка (имя_каталога) файлов)

С bash4.4+ и GNU findили findбольшинством BSD вы можете сделать что-то подобное с помощью:

shopt -s nullglob dotglob
readarray -td '' dirs < <(
  LC_ALL=C find . -depth -regex '.*\(/[^/]*\)\1' -type d -print0
)
for d in "${dirs[@]}"; do
  contents=("$d"/*)
  (( ${#contents[@]} == 0 )) ||
    mv -- "${contents[@]}" "${d%/*}/" &&
    rmdir -- "$d"
done

На этот раз для сопоставления файлов используется регулярное выражение ./path/to/dir/dirс использованием базовых обратных ссылок на регулярные выражения.

решение2

Попробуйте это, основанное на GNU findv4.8.0 и Bash v5.1.8

Часть 1: Анализ дерева каталогов + обнаружение дубликатов имен подкаталогов

Предположим, что определенный каталог в вашем дереве имеет следующую структуру:

./
|__test1/
     |__dirname with space
     |           |__test2
     |                |__ test2
     |__dirname **
     |       |__test1
     |
     |__reboot
     |     |__test1
     | 
     |__test2/
          |__test3/
               |__test2/
                    |__test1/
                         |__test1/

(Странные имена каталогов приведены для демонстрации безопасности кода.)

Вы видите, что некоторые подкаталоги (subdirs) повторяются по-разному. Некоторые повторяются несколько раз, а не один раз (например test1), один не повторяется ( test3), и они могут повторяться как родительские и дочерние или разделяться произвольным числом промежуточных подкаталогов.

Приведенный ниже код подробно раскрывает дубликаты имен подкаталогов в структуре каталогов.

  • он анализирует файловое дерево на предмет структуры подкаталогов, начиная с$PWD
  • он находит дубликаты для каждого компонента любого пути подкаталога из 2 или более уровней, не считая корневого уровня, который является $PWD. В моем эксперименте самый длинный путь подкаталога: ./test1/test2/test1/test3/test2/test1/test1
  • он выводит первый дубликат подкаталога, найденный на каждом уровне подкаталога, начиная с листа, т.е. читая путь подкаталога справа налево.
  • Печать перенаправляется в файл в обратном порядке, поэтому сначала отображается самый длинный путь подкаталога. Две последовательные точки с запятой разделяют компоненты пути (слева от ";;"), от первого дубликата (справа от ";;"), найденного в соответствии с предыдущим пунктом.

[Код]

$ find ./* -type d -exec bash -c 'set -o noglob; IFS="/" subdir=($(printf "%s " "$1")); dirlevels=$((${#subdir[@]}-1)); dupe="$(awk '\''!($1 in sd) {sd[$1];next} {print $1}'\'' < <(printf "%s\n" ${subdir[@]:1}))";[ $dirlevels -ge 2 ] && [ ! -z "$dupe"  ] && (printf "%s/" "${subdir[@]:1}";printf " ;; %s\n" "$(tail -n 1 < <(printf "%s\n" "$dupe"))";)' shellexec {} \; | tac >| tmp.data

$ cat -n  tmp.data

1 test1/reboot/test1/ ;; test1
2 test1/dirname with space/test2/test2/ ;; test2
3 test1/test2/test1/test3/test2/test1/test1/ ;; test1
4 test1/test2/test1/test3/test2/test1/ ;; test1
5 test1/test2/test1/test3/test2/ ;; test2
6 test1/test2/test1/test3/ ;; test1
7 test1/test2/test1/ ;; test1
8 test1/dirname **/test1/ ;; test1

Часть 2: Обработка дубликатов имен подкаталогов; перемещение содержимого

Обработка происходит в порядке, указанном в tmp.data.

  • в tmp.dataпервой строке первое имя dupe в пути ./test1/test2/test1/test3/test2/test1/test1test1. Мы можем перенести его содержимое в самый левый уровень подкаталога с тем же именем:./test1/
  • После перемещения содержимого без затирания существующих файлов в месте назначения самый правый уровень подкаталога test1удаляется.
  • переходим к строке 2 tmp.dataи повторяем вышеуказанные шаги.
  • и т.д., пока все линии не tmp.dataбудут израсходованы.

На этом этапе вопрос (автору вопроса: @TomDerks) заключается в том, что делать с крайней правой частью test1/*строки 6?всеего содержимое будет перемещено в самый левый каталог с тем же именем, который в данном случае является первым уровнем подкаталога в пути? Включает ли "все" файлы в./test1/test2/test1/ а такжеподкаталог test3и его содержимое?
Полное решение (часть 2) зависит от этого.

Связанный контент