Spłaszczanie zagnieżdżonego katalogu


71

Jest to prawdopodobnie bardzo proste, ale nie mogę tego rozgryźć. Mam taką strukturę katalogów (dir2 znajduje się w dir1):

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

Jaki jest najlepszy sposób „spłaszczenia” tej struktury dyrektora w taki sposób, aby uzyskać plik1 i plik2 w katalogu 1, a nie katalogu 2.


Odpowiedzi:


75

Możesz to zrobić za pomocą GNU findi GNU mv:

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

Zasadniczo sposób działa, jeśli findprzechodzi przez całe drzewo katalogów i dla każdego pliku ( -type f), który nie znajduje się w katalogu najwyższego poziomu ( -mindepth 2), uruchamia a, mvaby przenieść go do wybranego katalogu ( -exec mv … +). -tArgument mvpozwala określić katalog docelowy pierwszy, który jest potrzebny, ponieważ +forma -execumieszcza wszystkie lokalizacje źródłowe na końcu polecenia. Te -imarki mvzapytać przed zastąpieniem żadnych duplikatów; możesz -fje zastąpić bez pytania (lub -nnie pytaj ani nie zastępuj).

Jak zauważa Stephane Chazelas, powyższe działa tylko z narzędziami GNU (które są standardowe w Linuksie, ale nie w większości innych systemów). Poniższe jest nieco wolniejsze (ponieważ wywołuje mvwiele razy), ale o wiele bardziej uniwersalne:

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

3
Zmodyfikowano -exec +tak, aby nie wykonywał dużej liczby procesówmv
Random832

1
@ Random832 I zamiar przywrócić ponownie, ponieważ + nie działa. mvpotrzebuje miejsca docelowego jako ostatniego argumentu, ale + miałby źródła jako ostatni argument. Znajdź nie zaakceptuje nawet składni, którą zmieniłeś na ( find: missing argument to `-exec')
derobert

1
@ Random832, ale przypuszczam mv, -tże możemy użyć, więc zmienię to na to.
derobert

1
@Dom domyślnie finddrukuje ukryte pliki (kropkowe). Głębokość zależy od katalogu, który przekazujesz do znalezienia.
derobert

1
Lub w find ./dir -mindepth 2 -type f -exec mv -f '{}' ./dir ';'przypadku
zastąpienia

33

W Zsh:

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

**/rekursywnie przegląda podkatalogi. Glob kwalifikator . pasuje tylko zwykłe pliki, a Dgwarantuje, że pliki dot są włączone (domyślnie, pliki, których nazwa zaczyna się od .są wyłączone z zamiennika mecze). Aby wyczyścić teraz puste katalogi, uruchom rmdir dir1/**/*(/Dod)- /ogranicza się do katalogów i odnajpierw porządkuje głębokość dopasowań, aby usunąć je dir1/dir2/dir3wcześniej dir1/dir2.

Jeśli całkowita długość nazw plików jest bardzo duża, możesz napotkać ograniczenie długości linii poleceń. Zsh ma do pomocy poleceń wbudowanych mvi rmdirktóre nie są objęte tym ograniczeniem: uruchom zmodload zsh/filesje włączyć.

Tylko z narzędziami POSIX:

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

lub (szybciej, ponieważ nie musi uruchamiać osobnego procesu dla każdego pliku)

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

1
To powinna być zaakceptowana odpowiedź! Zwłaszcza w zwięzłej wersji zsh.
Adamski

3

Spróbuj to zrobić:

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

lub dla każdego pliku pasującego file[0-9]*w podkatalogu:

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

Zobacz http://mywiki.wooledge.org/glob


Powinienem był to wskazać, ale muszę mieć wiele plików do wykorzystania {}w moim prawdziwym problemie.
żółw

Zobacz moje drugie rozwiązanie
Gilles Quenot

Bingo Dzięki za pomoc. To zdecydowanie najlepsze rozwiązanie.
żółw

2

Napisałem dwie funkcje, których możesz używać razem, możesz to zrobić, możesz ograniczyć poziom katalogu, dodając -maxdepth $VALparametr.

# 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

Człowieku, po prostu niewłaściwie wykorzystałem twój skrypt, zapominając o argumentie ścieżki, który naprawdę spieprzył mój serwer. Ok, jestem facetem, który kopiuje i wkleja rzeczy i niewłaściwie ich używa, ale bądźcie mądrzy i dodawaj kontrole / potwierdzenia w skryptach, które usuwają / przenoszą takie
rzeczy

Ups! Przykro mi to słyszeć. Mam nadzieję, że masz kopię zapasową ... Dodałem potwierdzenie dla przyszłej ochrony.
Bruno,

Dzięki @Bruno jest znacznie lepiej w ten sposób. Mój serwer nadal działa bezbłędnie, skomentowałem część „spłaszczenia”, aby po prostu rekursywnie usunąć puste katalogi z (i to był mój błąd) roota, dopóki nie zobaczyłem błędu, który spowodował, że przestałem uruchamiać skrypt.
dulgan

1

Rozwijając popularną odpowiedź na to pytanie, ponieważ miałem przypadek użycia do spłaszczenia katalogu zawierającego pliki o tej samej nazwie.

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

W tym przypadku przekazana opcja -i( --interactive) mvnie przyniosłaby pożądanego rezultatu spłaszczenia struktury katalogów i obsługi konfliktów nazw. Więc to jest po prostu zastąpione --backup=t(równoważne --backup=numbered). Więcej dokumentacji na temat opcji -b( --backup) jest dostępnych na https://www.gnu.org/software/coreutils/manual/coreutils.html#Backup-options .

Wynikające z:

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

Co daje:

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

1

zarówno tar, jak i zip mają możliwość włączenia, a następnie usunięcia struktury katalogów, dzięki czemu mogłem szybko spłaszczyć zagnieżdżony katalog za pomocą

tar -cvf all.tar *

a następnie przeniesienie all.tar do nowej lokalizacji

tar -xvf all.tar --strip=4

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.