usuń plik, ale wyklucz wszystkie pliki z listy

17

Muszę okresowo czyścić folder. Dostaję listę plików, która zawiera tekst, które pliki są dozwolone. Teraz muszę usunąć wszystkie pliki, których nie ma w tym pliku.

Przykład:

dont-delete.txt:

dontdeletethisfile.txt
reallyimportantfile.txt
neverdeletethis.txt
important.txt

Mój folder do czyszczenia zawiera to jako przykład:

ls /home/me/myfolder2tocleanup/:

dontdeletethisfile.txt
reallyimportantfile.txt
neverdeletethis.txt
important.txt
this-can-be-deleted.txt
also-waste.txt
never-used-it.txt

Więc te pliki powinny zostać usunięte:

this-can-be-deleted.txt
also-waste.txt
never-used-it.txt

Przeszukuję coś, aby utworzyć polecenie usuwania z opcją wykluczenia niektórych plików zawartych w pliku.

stefan83
źródło
Czy to zadanie domowe?
mook765,
Mam nadzieję, że nie jesteś jego nauczycielem. lol
Gujarat Santana
2
@gujarat Nie jesteśmy bezpłatną pracą domową, więc komentarz jest uzasadniony. Jeśli chodzi o samo pytanie, może być przydatne dla innych, więc do tej pory jest otwarte.
Sergiy Kolodyazhnyy
@Serg Całkowicie się z tobą zgadzam
Gujarat Santana

Odpowiedzi:

9

rmKomenda jest wypowiedziało się tak, że można sprawdzić i upewnić się, że wszystko działa jak potrzeba. Następnie po prostu cofnij komentarz do tej linii.

check directorySekcja zapewni przypadkowo nie uruchomić skrypt z niewłaściwego katalogu i sprać niewłaściwych plików.

Możesz usunąć echo deletinglinię, aby uruchomić cicho.

#!/bin/bash

cd /home/me/myfolder2tocleanup/

# Exit if the directory isn't found.
if (($?>0)); then
    echo "Can't find work dir... exiting"
    exit
fi

for i in *; do
    if ! grep -qxFe "$i" filelist.txt; then
        echo "Deleting: $i"
        # the next line is commented out.  Test it.  Then uncomment to removed the files
        # rm "$i"
    fi
done
LD James
źródło
Zredagowałem twój kod, aby uniknąć niepotrzebnego użycials i bezużytecznego przechwytywania danych wyjściowych, grepjeśli wszystko, co chcesz wiedzieć, to czy było dopasowanie, czy nie. Użyłem również stałych wzorców, aby uniknąć problemów związanych z ucieczką.
David Foerster,
@DavidFoerster Dzięki za wkład. Jednak po zmianie whilepętli na forpętlę nieumyślnie zmieniłeś opcję iteration keyz ina f. w deklaracji, która złamała kod. Naprawiłem to.
LD James
Ups, siła nawyku. Skracam nazwy zmiennych powłoki dla nazw plików jako f. ;-P (… i +1 za twoją odpowiedź, o której wcześniej zapomniałem.)
David Foerster,
10

Ten skrypt Pythona może to zrobić:

#!/usr/bin/env python3
import os
no_remove = set()
with open('./dont-delete.txt') as f:
     for line in f:
         no_remove.add(line.strip())

for f in os.listdir('.'):
    if f not in no_remove:
        print('unlink:' + f ) 
        #os.unlink(f)

Ważną częścią jest odkomentowanie os.unlink()funkcji.

UWAGA : dodaj ten skrypt i dont-delete.txtswój dont-delete.txt, aby oba były na liście i trzymaj je w tym samym katalogu.

Sergiy Kolodyazhnyy
źródło
1
Zmieniłem twój kod, aby setw drugiej części zamiast listy O (1) zamiast wyszukiwania O (n) był używany .
David Foerster,
dziękuję za pomoc, zwykle jestem facetem od systemu Windows, ale szwy pythonowe też są fajne =)
stefan83
1
@ stefan83: Python działa równie dobrze w systemie Windows.
David Foerster
3

Oto jedna linijka:

comm -2 -3 <(ls) <(sort dont_delete) | tail +2 | xargs -p rm
  1. ls drukuje wszystkie pliki w bieżącym katalogu (w posortowanej kolejności)
  2. sort dont_delete drukuje wszystkie pliki, których nie chcemy usuwać w posortowanej kolejności
  3. <()operator włącza ciąg do obiektu plikopodobny
  4. Te commpolecenia porównuje dwa wstępnie posortowane pliki i wydruki na liniach, na których one się różnić
  5. użycie -2 -3flag powoduje commdrukowanie tylko wierszy zawartych w pierwszym pliku, ale nie w drugim, co będzie listą plików, które można bezpiecznie usunąć
  6. tail +2połączenie jest po prostu usunąć nagłówek commwyjście, które zawiera nazwę pliku wejściowego
  7. Teraz otrzymujemy listę plików do usunięcia przy standardowym wyjściu. Pipujemy to wyjście, do xargsktórego zamieni strumień wyjściowy w listę argumentów rm. Te -psiły opcja xargsprosić o potwierdzenie przed wykonaniem.
ogrodnik
źródło
dziękuję za pomoc, teraz mam moje rozwiązanie!
stefan83
@gardenhead, zmęczyłem twój kod, ale usuwa wszystkie pliki w katalogu i pozostawia tylko pierwszy i ostatni plik na liście dont-delete. masz jakiś pomysł na ten problem? z góry dziękuję.
Negar
1

FWIW wygląda na to, że możesz to zrobić natywnie zsh , używając (+cmd)kwalifikatora glob.

Aby to zilustrować, zacznijmy od niektórych plików

 % ls
bar  baz  bazfoo  keepfiles.txt  foo  kazoo

i plik białej listy

 % cat keepfiles.txt
foo
kazoo
bar

Najpierw przeczytaj białą listę do tablicy:

 % keepfiles=( "${(f)$(< keepfiles.txt)}" )

a może lepiej

 % zmodload zsh/mapfile
 % keepfiles=( ${(f)mapfile[./keepfiles.txt]} )

(odpowiednik basha mapfile wbudowanego - lub jego synonim readarray). Teraz możemy sprawdzić, czy w tablicy istnieje klucz (nazwa pliku), ${keepfiles[(I)filename]}który zwraca 0, jeśli nie znaleziono dopasowania:

 % print ${keepfiles[(I)foo]}
1
 % print ${keepfiles[(I)baz]}
0
 %

Możemy użyć tego do utworzenia funkcji, która zwraca true jeśli nie ma żadnych dopasowań dla $REPLYtablicy:

% nokeep() { (( ${keepfiles[(I)$REPLY]} == 0 )); }

Na koniec używamy tej funkcji jako kwalifikatora w naszym poleceniu:

 % ls *(+nokeep)
baz  bazfoo  keepfiles.txt

lub w twoim przypadku

 % rm -- *(+nokeep)

(Prawdopodobnie będziesz chciał dodać nazwę samego pliku białej listy do białej listy).

steeldriver
źródło
0

Zakładając, że twoja powłoka bash ma extglob shoptwłączone, oto nieco bardziej konserwatywna alternatywa:

rm !($(tr \\n \| < keep.txt))

(... towarzyszące doskonałej sugestii komunikacyjnej @ gardenhead!)

conny
źródło
0

Chyba że wynik ls /home/me/myfolder2tocleanup/przekracza maksymalny limit argumentów powłoki, ARG_MAX który wynosi około 2 MB dla Ubuntu , proponuję następujące.


Implementacja polecenia w jednym wierszu, która wykona zadanie, wyglądałaby następująco:

  1. Skopiuj dont-delete.txtplik do katalogu zawierającego pliki do usunięcia w następujący sposób:
cp dont-delete.txt /home/me/myfolder2tocleanup/
  1. cd do katalogu zawierającego pliki do usunięcia w następujący sposób:
cd /home/me/myfolder2tocleanup/
  1. Wykonaj próbę rozruchu, aby przetestować polecenie i wydrukować nazwy wykrytych plików, które mają zostać usunięte, bez faktycznego ich usuwania, w następujący sposób:
ls -p | grep -v / | sed 's/\<dont-delete.txt\>//g' | sort | comm -3 - <(sort dont-delete.txt) | xargs echo | tr " " "\n"
  1. Jeśli wyniki są zadowalające, usuń pliki, uruchamiając polecenie w następujący sposób:
ls -p | grep -v / | sed 's/\<dont-delete.txt\>//g' | sort | comm -3 - <(sort dont-delete.txt) | xargs rm

Wyjaśnienie:

  • ls -pwyświetli listę wszystkich plików i katalogów w bieżącym katalogu, a opcja -pdoda a /do nazw katalogów.
  • grep -v /wykluczy katalogi, usuwając wszystkie elementy zawierające /w ich nazwach.
  • sed 's/\<dont-delete.txt\>//g'wykluczy dont-delete.txtplik, aby nie został usunięty w trakcie procesu.
  • sort, dla pewności posortuje pozostałe dane wyjściowe ls.
  • comm -3 - <(sort dont-delete.txt)posortuje dont-delete.txtplik, porówna go z posortowanym wyjściem lsi wykluczy nazwy plików, które istnieją w obu.
  • xargs rmusunie wszystkie pozostałe nazwy plików z już przetworzonych danych wyjściowych ls. Oznacza to, że wszystkie elementy w bieżącym katalogu zostaną usunięte, z wyjątkiem katalogów , plików wymienionych w dont-delete.txtpliku i samego dont-delete.txtpliku

W części próbnej:

  • xargs echo wydrukuje pliki, które powinny zostać usunięte.
  • tr " " "\n" przekształci spacje w nowe wiersze dla łatwiejszej czytelności.
Raffa
źródło
0

Zdecydowanie sugeruję użycie rsynczamieszczonego tutaj rozwiązania ; w przeciwnym razie zastosuj poniższe rozwiązanie ze wspomnianym wyjątkowym warunkiem.

Zakładając, że nie ma białych znaków (spacji / tabulatorów) w twoich plikach wymienionych w pliku o nazwie excludelist, wtedy zrobiłbyś:

find /path/to -type f \( ! -name "excludelist" $(printf ' -a ! -name %s\n' $(< excludelist)) \)

Wystarczy dodać -deletedo powyższego polecenia, aby usunąć pliki, które nie istnieją w pliku listy wykluczeń . Jeśli tu nie ma -deleteopcji można korzystać rmz -execjak następuje:

find /path/to -type f \( ! -name "excludelist" $(printf ' -a ! -name %s\n' $(< excludelist)) \) -exec echo rm {} \;

Lub korzystając -execz +terminatorem zamiast.

find /path/to -type f \( ! -name "excludelist" $(printf ' -a ! -name %s\n' $(< excludelist)) \) -exec echo rm {} +

echo służy tylko do pracy na sucho.

αғsнιη
źródło
-1

Moja sugestia to:

sed -e 's/^/\.\//' dont-delete.txt > dont-delete-relative-path.txt
find . -type f -print | grep -Fxvf dont-delete-relative-path.txt | xargs -d'\n' rm

Aktualizacja 2018-08-07

Przykład:

1: mkdir /tmp/delete-example && cd /tmp/delete-example
2: touch a b c d
3: echo "./a\n./b\n./dont-delete.txt\n" > dont-delete.txt
4: find . -type f -print | grep -Fxvf dont-delete.txt | xargs -d'\n' rm

Uwaga po wierszu 3 będziesz mieć dont-delete.txtplik z zawartością:

./a
./b
./dont-delete.txt

(prowadzenie ./jest bardzo ważne )

Pliki ci dzostaną usunięte.

nyxz
źródło
Próbowałem tego z plikiem tekstowym o nazwach plików oddzielonych znakiem nowej linii. Skończyło się to usunięciem wszystkich plików w katalogu.
Jacques MALAPRADE
Chyba twoja „lista kontrolna” była błędna.
nyxz
Dodałem przykładowe użycie.
nyxz