Usuń wszystkie pliki w katalogu, którego nazwa nie pasuje do linii na liście plików

9

Mam katalog z ponad 1000 plików. W pliku tekstowym mam około 50 nazw plików, po jednym w wierszu. Chciałbym usunąć wszystkie pliki w katalogu, których nazwy plików nie odpowiadają wpisowi na liście. Jak najlepiej to zrobić? Uruchomiłem skrypt powłoki, ale nie mogłem określić, czy na liście znajduje się odpowiednie polecenie, które można określić w nazwie pliku. Dzięki.

Nathan
źródło

Odpowiedzi:

8

Zdaję sobie sprawę, że każde pytanie dotyczące usuwania plików należy rozwiązywać bardzo ostrożnie. Moja pierwsza odpowiedź była zbyt pochopna. Nie wziąłem pod uwagę, że lista plików może być zniekształcona do użycia z egrep. Zredagowałem odpowiedź, aby zmniejszyć to ryzyko.

To powinno działać dla plików, które nie mają spacji w nazwie:

Najpierw odbuduj listę plików, aby mieć pewność, że dokładnie pasuje do nazwy pliku:

sed -e 's,^,^,' -e 's,$,$,'  filelist  > newfilelist 

buduj komendy rm

cd your_directory
ls | egrep -vf newfilelist   | xargs -n 1 echo rm  >  rmscript

Sprawdź, czy skrypt rm Ci odpowiada (Możesz to zrobić za pomocą „vim” lub „mniej”).
Następnie wykonaj akcję:

sh -x rmscript

Jeśli pliki mają spacje w nazwie (jeśli pliki mają "w nazwie to to nie zadziała):

ls | egrep -vf newfilelist  | sed 's,^\(.*\)$,rm "\1",' > rmscript

oczywiście lista plików nie powinna znajdować się w tym samym katalogu!

EDYTOWANE:

Lista plików Nathana zawierała nazwy pasujące do wszystkich plików w katalogu (np. „Html” pasuje do „bob.html”). Więc nic nie zostało usunięte, ponieważ egrep -vfpochłonęło cały strumień. Dodałem polecenie umieszczenia „^” i „$” wokół nazwy każdego pliku. Miałem szczęście, że lista akt Nathana była poprawna. Gdyby był sformatowany w systemie DOS z zakończonymi liniami CR-LF lub z dodatkowymi spacjami, żadne pliki nie byłyby zachowane przez egrep i wszystkie zostałyby usunięte.

Emmanuel
źródło
Kiedy uruchamiam polecenie podglądu, otrzymuję jedną linię z „rm”. Po uruchomieniu rzeczywistego polecenia pojawia się komunikat o błędzie dotyczący brakujących argumentów dla rm. Czy potrzebuję specjalnej składni, aby użyć wyników z ls | egrep w danych wejściowych xargs?
Nathan
@ Natan musisz najpierw cd do katalogu. Brak specjalnej składni. lszapewnia nazwy plików katalogów, egrep -vf filelistodfiltruj 50 nazw plików. Obawiam się, że usunąłeś wszystkie swoje pliki.
Emmanuel
@Emamanuel Uruchamiam polecenie z katalogu zawierającego pliki do usunięcia.
Nathan
@Nathan czy wszystkie twoje pliki zostały usunięte?
Emmanuel
nie, wciąż tam są.
Nathan
1

Wstępnie konstruuj argumenty, aby find:

{
  read -r
  keep=( -name "$REPLY" ) # no `-o` before the first one.
  while read -r; do
    keep+=( -o -name "$REPLY" )
  done
} < file_list.txt
find . -type f ! \( "${keep[@]}" \) -exec echo rm {} +

Użyj echoczęści, aby zobaczyć, co zostanie zbudowane. Wyjmij echoczęści, aby faktycznie je uruchomić.

Aktualizacja: Demonstracja:

##
# Demonstrate what files exist for testing.
# Show their whitespace:
~/foo $ printf '"%s"\n' *
" op"
" qr"
"abc"
"def"
"gh "
"ij "
"k l"
"keep"
"m n"

##
# Show the contents of the "keep" file,
# Including its whitespace:
~/foo $ cat -e keep
keep$
abc$
gh $
k l$
 op$

##
# Execute the script:
~/foo $ { read -r; keep=( -name "$REPLY" ); while read -r ; do keep+=( -o -name "$REPLY" ); done } < keep
~/foo $ find . -type f ! \( "${keep[@]}" \) -exec rm {} +

##
# Show what files remain:
~/foo $ printf '"%s"\n' *
" op"
"abc"
"gh "
"k l"
"keep"
kojiro
źródło
Najbardziej podoba mi się ten, ponieważ usuwa potrzebę listy plików
eyoung100
+1 ode mnie, chociaż nie radzi sobie zbyt dobrze ze spacjami. Być może niektórzy apostrofów ( ') należy dodać to znaczy keep=( -name \'"$REPLY"\' )a keep+=( -o -name \'"$REPLY"\' ).
Cristian Ciupitu
powyższe jest niebezpieczne, ponieważ możesz przypadkowo usunąć pliki.
davidva
@CristianCiupitu, prawda? Dodałem demo pokazujące, że bardzo dobrze radzi sobie z białymi spacjami.
kojiro
@davidva W jakich okolicznościach? Za każdym razem, gdy zautomatyzujesz usuwanie rzeczy, ryzykujesz popełnienie błędu, ale w ramach parametrów pytania myślę, że moje demo dowodzi, że takie podejście jest słuszne.
kojiro
1

Z zsh:

mylist=(${(f)"$(<filelist)"})
print -rl -- *(.^e_'(($mylist[(Ie)$REPLY]))'_)

Odczytuje wiersze z filelisttablicy, a następnie używa glob qualifiers / estring do glob / zaznacz tylko te nazwy plików, których nie ma w tablicy: .zaznacza tylko zwykłe pliki (dodaj, Djeśli twoja lista zawiera pliki kropkowe), a negacja ^e_'expression'_dalej wybiera tylko te dla którego wyrażenie zwraca false, tzn. jeśli ich nazwa ( $REPLY) nie jest elementem tablicy .
Jeśli jesteś zadowolony z wyniku zastąpienia print -rlz rmfaktycznie usunąć pliki:

rm -- *(.^e_'(($mylist[(Ie)$REPLY]))'_)

Aby rekurencyjnie wybierać i usuwać pliki, użyj */**glob z ${REPLY:t}modyfikatorem glob:

rm -- */**(.^e_'(($mylist[(Ie)${REPLY:t}]))'_)
don_crissti
źródło
0

Jeśli umieścisz zawartość katalogu w takim pliku:

cd <somedirectory>
ls >> filelist

Otwórz listę plików za pomocą edytora tekstu i usuń wszystkie pliki oprócz tych, które CHCESZ USUNĄĆ . To jest pogrubione, ponieważ jest to odwrotne podejście do powyższej odpowiedzi

Spróbuj tego:

while read p || [[ -n $p ]]; 
echo $p
done < filelist

Jeśli zobaczysz listę plików wyprowadzanych na ekran, zamień echo na rm -v:

while read p || [[ -n $p ]]; 
rm -v $p
done < filelist
eyoung100
źródło
0

Uruchom poniższy skrypt.

  1. Początkowo znajduję wszystkie pliki znajdujące się w katalogu i przechowuję dane wyjściowe w innym pliku all_files.
  2. Mamy plik z listą plików, których NIE należy usuwać ( not_to_be_deleted_files).
  3. Dodam nazwy plików not_to_be_deleted_filesi files_to_be_deletedna koniec, not_to_be_deleted_filesponieważ potrzebujemy tych 2 plików.
  4. Teraz znajduję pliki, które należy usunąć za pomocą joinpolecenia linux i przekierowuję dane wyjściowe do files_to_be_deleted pliku.
  5. Teraz w ostatniej pętli while czytam wszystkie nazwy plików files_to_be_deletedi usuwam pliki wymienione w tej nazwie pliku.

Skrypt jest jak poniżej.

find /home/username/directory -type f | sed 's/.*\///' > all_files
echo all_files >> not_to_be_deleted_files
echo not_to_be_deleted_files >> not_to_be_deleted_files
echo files_to_be_deleted >> not_to_be_deleted_files
join -v 1 <(sort all_files_listed) <(sort files_not_to_be_deleted) >   files_to_be_deleted
while read file
rm  "$file"
done < files_to_be_deleted

PS : Prawdopodobnie, jeśli chcesz to zapisać jako skrypt i uruchomić, możesz także dodać nazwę skryptu, używając echo scriptname >> not_to_be_deleted_files.

Chociaż nie jest to wymagane, wolę to zrobić, ponieważ później nie będzie żałować. Testowałem mały zestaw plików i działał on w moim systemie. Jeśli jednak chcesz się upewnić, spróbuj testnajpierw w katalogu, a następnie usuń pliki z oryginalnego katalogu.

Ramesh
źródło
0
  • Użyj listy jako źródła, aby przenieść wszystkie pliki z listy do nowego, nowego i pustego katalogu zapisu.
  • Porównaj liczbę plików na liście i liczbę zapisanych plików.
  • Jeśli oba są zgodne, usuń wszystkie niezapisane pliki za pomocą ulubionej metody.
  • Przenieś zapisane pliki z powrotem.
nieznany użytkownik
źródło
0

Wybrałem bezpieczniejsze i znacznie szybsze podejście, ponieważ miałem na liście 18 000 plików! Musiałem wyczyścić obrazy w dużej instalacji Drupala.

Usunięcie wszystkich plików, których nie ma na liście, jest tym samym, co zachowanie tylko tych, które są na liście. Postanowiłem więc skopiować pliki z listy do innej lokalizacji, ale skopiowanie 20 GB plików zajęłoby zbyt dużo miejsca i byłoby bardzo powolne. Sztuką jest więc skopiowanie plików hardlinkszamiast tego, używając -lopcji cp. To nie zajmuje prawie miejsca i jest bardzo szybkie. Dodatkowo, ponieważ musiałem zachować strukturę katalogów, skorzystałem z tej --parentsopcji.

Oto fragment mojej listy plików:

1px.png
misc/feed.png
modules/file/icons/x-office-presentation.png
modules/file/icons/x-office-spreadsheet.png
newsletter.png
sites/all/libraries/ckeditor/plugins/smiley/images/devil_smile.png
sites/all/libraries/ckeditor/plugins/smiley/images/regular_smile.png
sites/default/files/009313_PwC_banner_CBS_Observer_180x246px.jpg

Przykładem może być wiersz z tempem jako miejscem docelowym:

cp -l --parents 'misc/feed.png' temp

Spowoduje to utworzenie tej struktury:

temp
  misc
    feed.png

Zauważ, że miejsce docelowe musi znajdować się w tym samym systemie plików co źródło twardych dowiązań do działania.

Następnym krokiem jest zbudowanie skryptu:

sed -e "s,^,cp -l --parents '," -e "s,$,' /some/where/temp," filelist > newfilelist

Teraz, zakładając, że utworzyłeś już pusty katalog / some / where / temp, możesz skopiować pliki w następujący sposób:

sh newfilelist 2> missing_files

Zwróć uwagę, w jaki sposób błędy się kończą missing_files. Dodatkową zaletą tego podejścia jest to, że otrzymasz listę plików z oryginalnej listy, które tak naprawdę nie istnieją!

Po uruchomieniu skryptu temp będzie zawierać tylko te pliki, które znajdują się na liście plików, ale bez usuwania czegokolwiek i bez zajmowania dodatkowego miejsca. Jeśli wynik jest zadowalający, możesz usunąć wszystkie oryginalne pliki, w tym podfoldery.

Na koniec przenieś pliki i foldery z temp z powrotem do pierwotnej lokalizacji.

W przypadku 18 000 plików zajęło to tylko kilka sekund.

marlar
źródło
0

Bezpiecznie, prosto.

cd do katalogu.

Utwórz katalog tymczasowy.

mv *.yourExlusionSelector.* ./temp
rm *
mv ./temp ./
rm -rf ./temp

gotowy.

paradisaeidae
źródło
Witamy na stronie. Chociaż twoje podejście będzie działać, jeśli nazwy na liście wspomnianej przez PO są wynikiem prostego dopasowania wzorca - co może być bardzo prawdopodobne - pamiętaj, że PO stwierdził, że nazwy plików do wykluczenia są przechowywane w określonym pliku; możesz rozszerzyć swoją odpowiedź, aby odczytać wzorce wykluczeń z tego pliku zamiast polegać na jednym schemacie statycznym lub konieczności kopiowania potencjalnie wielu wzorców do konsoli.
AdminBee