Вирівнювання вкладеного каталогу


71

Це, мабуть, дуже просто, але я не можу цього зрозуміти. У мене така структура каталогів (dir2 знаходиться всередині dir1):

/dir1
    /dir2 
       |
        --- file1
       |
        --- file2

Який найкращий спосіб «розрівняти» цю структуру директора таким чином, щоб отримати file1 та file2 у dir1, а не dir2.


Відповіді:


75

Це можна зробити за допомогою GNU findта GNU mv:

find /dir1 -mindepth 2 -type f -exec mv -t /dir1 -i '{}' +

В основному спосіб, який працює, якщо він findпроходить через усе дерево каталогів і для кожного файлу ( -type f), який не знаходиться в каталозі верхнього рівня ( -mindepth 2), він запускає a, mvщоб перемістити його до потрібного каталогу ( -exec mv … +). -tАргумент mvдозволяє вказати каталог призначення першим, який необхідний , тому що +форма -execпоміщає всі місця джерела в кінці команди. В -iмарці mvзапитати перед перезаписом дублікатів; ви можете замінити, -fщоб перезаписати їх, не запитуючи (або -nне запитувати чи перезаписувати).

Як зазначає Стефан Шазелас, вищезгадане працює лише з інструментами GNU (які є стандартними для Linux, але не для більшості інших систем). Далі дещо повільніше (бо викликає mvкілька разів), але набагато універсальніше:

find /dir1 -mindepth 2 -type f -exec mv -i '{}' /dir1 ';'

3
Відредаговано для використання, -exec +щоб воно не виконувало велику кількість процесівmv
Random832

1
@ Random832 І повернутися знову, тому що + не працює. mvпотрібен пункт призначення як остаточний аргумент, але + матиме джерела як кінцевий аргумент. Знайдіть навіть не прийняти синтаксис, який ви змінили на ( find: missing argument to `-exec')
derobert

1
@ Random832, але я вважаю mv, що -tми можемо використовувати, тому я зміню його на це.
derobert

1
@Dom findдрукує приховані (крапкові) файли за замовчуванням. Глибина відносно каталогу, який ви переходите до пошуку.
дероберт

1
Або find ./dir -mindepth 2 -type f -exec mv -f '{}' ./dir ';'якщо переважні дублікати
Atav32

33

В zsh:

mv dir1/*/**/*(.D) dir1

**/перетинає підкаталоги рекурсивно. Глоба відбіркові . матчі звичайних файлів тільки, і Dгарантує , що точка файли включені (за замовчуванням, файли, ім'я яких починається з .виключені з шаблону відповідає). Щоб після цього очистити порожні каталоги, запустіть rmdir dir1/**/*(/Dod)- /обмежує каталоги та odспочатку накаже глибину збігу, щоб видалити dir1/dir2/dir3раніше dir1/dir2.

Якщо загальна довжина імен файлів дуже велика, ви можете зіткнутися з обмеженням довжини командного рядка. Zsh має вбудовані команди для mvі rmdirякі не страждають від цього обмеження: бігти , zmodload zsh/filesщоб включити їх.

За допомогою лише інструментів POSIX:

find dir1 -type f -exec mv {} dir1 \;
find dir1 -depth -exec rmdir {} \;

або (швидше, оскільки не потрібно запускати окремий процес для кожного файлу)

find dir1 -type f -exec sh -c 'mv "$@" dir1' _ {} +
find dir1 -depth -exec rmdir {} +

1
Це має бути прийнята відповідь! Особливо з лаконічною версією zsh.
Адамський

3

Спробуйте зробити це:

cp /dir1/dir2/file{1,2} /another/place

або для кожного файлу, що збігається file[0-9]*у підкаталозі:

cp /dir1/dir2/file[0-9]* /another/place

Дивіться http://mywiki.wooledge.org/glob


Я мав би це вказати, але мені доведеться використовувати багато файлів {}у своїй реальній проблемі.
черепаха

Дивіться моє друге рішення
Жилль Кінот

Бінго. Дякую за допомогу. Це, безумовно, найкраще рішення.
черепаха

2

Я написав дві функції, які ви можете використовувати разом, які роблять саме це, ви можете обмежити рівень каталогу, додавши -maxdepth $VALпараметр.

# This scripts flattens the file directory
# Run this script with a folder as parameter:
# $ path/to/script path/to/folder

#!/bin/bash

rmEmptyDirs(){
    local DIR="$1"
    for dir in "$DIR"/*/
    do
        [ -d "${dir}" ] || continue # if not a directory, skip
        dir=${dir%*/}
        if [ "$(ls -A "$dir")" ]; then
            rmEmptyDirs "$dir"
        else
            rmdir "$dir"
        fi
    done
    if [ "$(ls -A "$DIR")" ]; then
        rmEmptyDirs "$DIR"
    fi
}

flattenDir(){
    local DIR="$1"
    find "$DIR" -mindepth 2 -type f -exec mv -i '{}' "$DIR" ';'
}

read -p "Do you wish to flatten folder: ${1}? " -n 1 -r
echo    # (optional) move to a new line
if [[ $REPLY =~ ^[Yy]$ ]]
then
    flattenDir "$1" &
    rmEmptyDirs "$1" &
    echo "Done";
fi

Людина, я просто зловживав твоїм сценарієм, забувши аргумент шляху, який просто справді трахнув мій сервер. Гаразд, я хлопець, який копіює вставляти речі та зловживає ними, але хлопці, будьте мудрими та додайте чеки / підтвердження для скриптів, які видаляють / переміщують подібні
речі

Ого! Мені шкода це чути. Сподіваюся, у вас є резервна копія ... Я додав підтвердження для подальшого захисту.
Бруно

Дякую @Bruno, що набагато краще. Мій сервер все ще працює бездоганно, я прокоментував "сплюстити" частину, щоб просто видаляти порожні каталоги рекурсивно з кореня (і це була моя помилка), поки я не побачив помилку, яка змусила мене припинити запуск сценарію.
dulgan

1

Розширення на популярну відповідь на це питання, оскільки у мене був випадок використання для вирівнювання каталогу, що містить файли з однойменною назвою.

dir1/
├── dir2
   └── file
└── dir3
    └── file

У цьому випадку параметр -i( --interactive), переданий до mv, не дасть бажаного результату для вирівнювання структури каталогу та обробки конфліктів імен. Тож його просто замінюють --backup=t(еквівалентно --backup=numbered). Додатковішу інформацію про параметр -b( --backup) можна отримати на веб-сторінці https://www.gnu.org/software/coreutils/manual/coreutils.html#Backup-options .

Результат:

find dir1/ -mindepth 2 -type f -exec mv -t dir1/ --backup=t '{}' +

Який урожай:

dir1/
├── dir2
├── dir3
├── file
└── file.~1~

1

tar і zip мають можливість включити, а потім зняти структуру каталогів, тому мені вдалося швидко вирівняти вкладений каталог із

tar -cvf all.tar *

після чого перемістіть all.tar на нове місце потім

tar -xvf all.tar --strip=4

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.