Rekurencyjnie przenieś pliki w podkatalogach do nowych podkatalogów o tej samej nazwie

2

Mam partię plików, które kończą się tym samym ciągiem, tzn .: *_ext.datznajdują się w kilku podkatalogach wraz z kilkoma innymi plikami, w danym katalogu głównym. Oto struktura:

/main_dir/subdir1/file11_ext.dat
/main_dir/subdir1/file12_ext.dat
/main_dir/subdir1/file13_ext.dat
/main_dir/subdir1/file14_other.dat
/main_dir/subdir1/file15_other.dat

/main_dir/subdir2/file21_ext.dat
/main_dir/subdir2/file22_ext.dat
/main_dir/subdir2/file23_ext.dat
/main_dir/subdir2/file24_other.dat
/main_dir/subdir2/file25_other.dat

/main_dir/subdir3/file31_ext.dat
/main_dir/subdir3/file32_ext.dat
/main_dir/subdir3/file33_ext.dat
/main_dir/subdir3/file34_other.dat
/main_dir/subdir3/file35_other.dat

Muszę rekurencyjnie przenosić tylko pliki kończące *_ext.datsię na nowy katalog główny new_dir, z poszanowaniem struktury sub-katalogu, tak aby pliki skończyły się na równoważnej strukturze katalogu:

/new_dir/subdir1/file11_ext.dat
/new_dir/subdir1/file12_ext.dat
/new_dir/subdir1/file13_ext.dat

/new_dir/subdir2/file21_ext.dat
/new_dir/subdir2/file22_ext.dat
/new_dir/subdir2/file23_ext.dat

/new_dir/subdir3/file31_ext.dat
/new_dir/subdir3/file32_ext.dat
/new_dir/subdir3/file33_ext.dat

Z tego powodu polecenie powinno również utworzyć podkatalogi z odpowiadającymi im nazwami. Wiem o tym z taką linią:

find . -name "*_ext.dat" -print0 | xargs -0 rm -rf

Mogę usunąć wszystkie te pliki, ale nie wiem, jak je zmodyfikować, aby zrobić to, czego potrzebuję (lub czy jest to w ogóle możliwe).

Gabriel
źródło
1
Prawdopodobnie coś takiego załatwi sprawę rsync --include=*_ext.dat /main_dir/ /new_dir/. Nie przetestowałem tego jednak.
Jaap Eldering

Odpowiedzi:

1

Najpierw utworzę podkatalogi new_dirprzez

cd main_dir
for i in *; do mkdir "../new_dir/$i"; done
cd ..

Następnie można użyć bash„s forponownie komendę wraz z ekspansją wzór zrobić dokładnie to, co trzeba szybko:

for i in main_dir/*/*_ext.dat; do cp "$i" "new_dir${i##main_dir}"; done

wykorzystując fakt, że istnieją katalogi docelowe. Wreszcie, jeśli nie ma gwarancji, że każdy z nich zostanie rzeczywiście użyty, możesz wyczyścić puste:

cd new_dir
rmdir --ignore-fail-on-non-empty *
The Vee
źródło
W pierwszej pętli oczekujesz, że w main_dirniej są tylko katalogi. Nie wpisujesz podwójnie ścieżek w całym kodzie, więc nie będzie działać z nazwami plików zawierającymi spacje. Kod ten będzie znacznie bardziej wytrzymałe: for i in */; do mkdir "../new_dir/$i"; done. --- Ostatni fragment kodu nie usunie katalogów zawierających puste katalogi. Do tego możesz użyć find: Jak usunąć zagnieżdżone puste katalogi za pomocą skryptu Bash w systemie Linux?
pabouk
@pabouk Oczywiście, że tak. (Być może wcześniej tego nie zauważyłeś cd?) Strona podręcznika użytkownika rmdirmówi: „OPIS ... Usuń KATALOG (y), jeśli są puste”. Opcja --ignore-fail-on-non-emptyrezygnuje z ostrzeżeń, że niepuste katalogi zostały pominięte. Co więcej, wypróbowałem to na swoim systemie przed wysłaniem.
The Vee
Również struktura katalogów, których oczekują moje polecenia, doskonale zgadza się ze sposobem, w jaki OP ją wprowadziła, w tym z jego wyraźnymi przykładami. Oczywiście polecenia byłyby inne, gdyby struktura była.
The Vee
Dzięki za komentarz do spacji. Naprawiony.
The Vee
Zapraszamy. Przesadziłeś z podwójnymi cudzysłowami. Ostatnie polecenie nie zrobi tego, co chcesz. --- Pierwsza pętla: czy nie lepiej jest dodać pojedynczy, /aby kod był bardziej uniwersalny i działał w większej liczbie przypadków użycia? --- Ostatnie polecenie nie działa: mkdir a ; cd a ; mkdir -p a/b/c d/e/f g/h/i ; ls ; rmdir --ignore-fail-on-non-empty * ; ls--- wyjście:a d g a d g
pabouk 11.11.13