Сценарії оболонок UNIX: як рекурсивно переміщувати файли в один каталог?


8

У мене є велика кількість невеликих файлів, f, розташованих у такій структурі каталогу:

/A/B/C/f

Є 11 каталогів на рівні A, кожен з приблизно 100 каталогів на рівні B, кожен з приблизно 30 каталогів на рівні C, кожен з одним файлом f.

Як перемістити всі файли f вгору на один рівень? Наприклад, враховуючи цей набір файлів ...

/ A / B / C / f1
/ A / B / C / f2
/ A / B / C / f3
/ A / B / C / f4

Я хочу, щоб каталог /A/B/містив 4 файли, від f1 до f4. Видалення каталогів C не потрібно.

Я сподіваюся , що це розв'язувана проблема, можливо , з залученням find, xargsі whatnot. Будь-які ідеї?

Ура,

Джеймс

Відповіді:


9

Досить просто за допомогою пошуку GNU (як знайдено в Linux) або будь-якої іншої знахідки, яка підтримує -execdir:

find A -type f -execdir mv -i {} .. \;

За допомогою стандарту find:

find A -type f -exec sh -c 'mv -i "$1" "${1%/*}/.."' sh {} \;

З zsh:

zmv -Q -o-i 'A/(**/)*/(*)(.)' 'A/$1$2'

Якщо структура каталогів завжди має однаковий рівень вкладеності, вам не потрібно рекурсивного переходу (але спочатку видаліть порожні каталоги):

for x in */*; do; echo mv -i "$x"/*/* "$x"/..; done

1

Для цього набору файлів це:

$ cd /A/B/C/
$ mv ./* ../

Але я чекаю, що ваша проблема дещо складніша ... Я не можу відповісти на це ... Я не зовсім впевнений, як складається ваша структура режиму ... Чи можете ви уточнити?


Це, очевидно, спрацює для будь-якого одного каталогу / A / B / C, але оскільки у мене є близько 30000 таких каталогів, я сподіваюся на команду, яку можна виконати вгорі ієрархії, щоб опікуватися ними всіми одразу.
jl6

1
@ jl6 Я бачу, і переміщення потребують лише файли? Таким чином, C не потрібно переходити до B, а B не до A тощо ...
Переповнення стека мертве

Це правильно - операцію слід виконувати лише на файлах, на найнижчому рівні ієрархії, і переміщувати їх слід лише на один рівень вгору.
jl6

0

Перша моя здогадка

$ find A -type f -exec mv {} .. \;  

до тих пір, поки ви не вкажете -depthце повинно бути нормально. Я НЕ Спробував це, тому тестуйте його спочатку, перш ніж взяти на себе його.


Що робити, коли є два файли з однаковою назвою?

Знайти не проблема, вкладена команда "mv" є. Імена Dupe не було б добре, оскільки mv не перейменовується, він перезаписується. Ви можете використовувати mv -i, щоб трохи допомогти, але з 30 000+ файлами, які можуть бути болючими. Якщо вам потрібно мати такий контроль над ситуацією, вам може знадобитися програма, написана на мові сценаріїв (мій вибір буде python, YMMV).
готелі

Я не використовував "резервні копії" з телевізором, але дивлячись на сторінку чоловіка, схоже, це може бути вирішенням вашої проблеми з іменами дупа. "знайти A -тип f -exec mv - резервне копіювання {} .. \;" може спрацювати (зауважте, що це подвійний тире в
резервне копіювання

Наведений приклад пошуку перемістить усі файли на найнижчому рівні до каталогу, розташованого вище A (я вважаю, що ".." інтерпретується оболонкою, перш ніж передаватись для пошуку / mv?). Мені потрібні файли для переміщення лише на один рівень в ієрархії. Ще одне уточнення: дублюючих імен файлів не буде, доки файли рухаються лише НА ОДИН рівень вгору.
jl6

@hotei: mvКоманди виконуються в поточному каталозі, тому ..не виконують те, що ви сподіваєтесь. Дивіться мою відповідь щодо рішень. @ jl6: оболонка не має нічого спільного .., цим керує ядро.
Жил 'ТАК - перестань бути злим'

0

Якщо ви хочете переміщувати лише файли, що знаходяться в каталогах листів (тобто ви не хочете переходити /A/B/fileдо них, /Aякщо Bмістять підкаталоги), то ось кілька способів зробити це:

Обидва цього вимагають

leaf ()
{
    find $1 -depth -type d | sed 'h; :b; $b; N; /^\(.*\)\/.*\n\1$/ { g; bb }; $ {x; b}; P; D'
}
shopt -s nullglob

Цей працює:

leaf A | while read -r dir
do
    for file in "$dir"/*
    do
        parent=${dir%/*}
        if [[ -e "$parent/${file##*/}" ]]
        then
            echo "not moved: $file"
        else
            mv "$file" "$parent"
        fi
    done
done

Це буде швидше, але це не любить порожні каталоги:

leaf A | while read -r dir
do
    mv -n "${dir}"/* "${dir%/*}"
    remains=$(echo "$dir"/*)
    [[ -n "$remains" ]] && echo "not moved: $remains"
done
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.