rimuove il file ma esclude tutti i file in un elenco


17

Devo pulire periodicamente una cartella. Ottengo un elenco file che contiene testo, quali file sono consentiti. Ora devo eliminare tutti i file che non sono in questo file.

Esempio:

dont-delete.txt:

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

La mia cartella di pulizia contiene questo come esempio:

ls /home/me/myfolder2tocleanup/:

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

Quindi questi file dovrebbero essere eliminati:

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

Cerco qualcosa per creare un comando di eliminazione con un'opzione per escludere alcuni file forniti da file.


Questo è un compito?
Mook765,

Spero che tu non sia il suo insegnante. lol
Gujarat Santana,

2
@gujarat Non siamo un servizio di compiti a casa gratuito, quindi il commento è giustificato. Per quanto riguarda la domanda stessa, può essere utile per gli altri, quindi è aperto finora.
Sergiy Kolodyazhnyy,

@Serg Sono totalmente d'accordo con te
Gujarat Santana,

Risposte:


9

Il rmcomando è commentato in modo da poter verificare e verificare che funzioni come necessario. Quindi basta annullare il commento di quella riga.

La check directorysezione ti assicurerà di non eseguire accidentalmente lo script dalla directory sbagliata e di bloccare i file sbagliati.

È possibile rimuovere la echo deletinglinea per l'esecuzione silenziosa.

#!/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

Ho modificato il tuo codice per evitare l' uso inutilels e l'acquisizione inutile dell'output grepse tutto ciò che vuoi sapere è se c'è stata una corrispondenza o meno. Ho anche usato schemi a stringa fissa per evitare problemi di fuga.
David Foerster,

@DavidFoerster Grazie per il contributo. Tuttavia, quando hai cambiato il whileloop in un forloop hai inavvertitamente cambiato il iteration keyda ia f. nella dichiarazione, che ha infranto il codice. L'ho riparato.
LD James,

Oops, forza dell'abitudine. Tendo ad abbreviare i nomi delle variabili della shell per i nomi dei file come f. ;-P (... e +1 per la tua risposta che ho dimenticato prima.)
David Foerster,

10

Questo script Python può fare questo:

#!/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)

La parte importante è decommentare la os.unlink()funzione.

NOTA : aggiungi questo script e il dont-delete.txttuo in dont-delete.txtmodo che entrambi siano nell'elenco e tienili nella stessa directory.


1
Ho cambiato il tuo codice per utilizzare un al setposto di un elenco per la ricerca O (1) anziché O (n) nella seconda parte.
David Foerster,

grazie per il tuo aiuto, di solito sono un ragazzo di Windows, ma anche le cuciture in pitone sono cool =)
stefan83

1
@ stefan83: Python funziona altrettanto bene su Windows.
David Foerster,

3

Ecco un one-liner:

comm -2 -3 <(ls) <(sort dont_delete) | tail +2 | xargs -p rm
  1. ls stampa tutti i file nella directory corrente (in ordine ordinato)
  2. sort dont_delete stampa tutti i file che non vogliamo eliminare in ordine
  3. l' <()operatore trasforma una stringa in un oggetto simile a un file
  4. I commcomandi confronta due file preordinati e stampa le linee su cui differiscono
  5. l'uso dei -2 -3flag fa sì commche vengano stampate solo le righe contenute nel primo file ma non nel secondo, che sarà l'elenco dei file che è possibile eliminare in modo sicuro
  6. la tail +2chiamata è solo per rimuovere l'intestazione commdell'output, che contiene il nome del file di input
  7. Ora otteniamo un elenco di file da eliminare in uscita standard. Eseguiamo il pipe di questo output al xargsquale trasformeremo il flusso di output in un elenco di argomenti per rm. L' -popzione forza xargsa chiedere conferma prima dell'esecuzione.

grazie per il tuo aiuto, ora ho la mia soluzione!
stefan83,

@gardenhead, ho stanco il tuo codice ma rimuove tutti i file nella directory e conserva solo il primo e l'ultimo file nell'elenco non eliminare. hai idea di questo problema? Grazie in anticipo.
Negar,

1

FWIW sembra che tu possa farlo nativamente zsh, usando il (+cmd)qualificatore glob.

Per illustrare, iniziamo con alcuni file

 % ls
bar  baz  bazfoo  keepfiles.txt  foo  kazoo

e un file della whitelist

 % cat keepfiles.txt
foo
kazoo
bar

Innanzitutto, leggi la lista bianca in un array:

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

o forse meglio

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

(l'equivalente del mapfilebuiltin di bash - o il suo sinonimo readarray). Ora possiamo verificare se esiste una chiave (nome file) nell'array usando ${keepfiles[(I)filename]}quale restituisce 0 se non viene trovata alcuna corrispondenza:

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

Possiamo usarlo per creare una funzione che ritorni truese non ci sono corrispondenze $REPLYnell'array:

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

Infine, usiamo questa funzione come qualificatore nel nostro comando:

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

o, nel tuo caso

 % rm -- *(+nokeep)

(Probabilmente vorrai aggiungere il nome del file della whitelist stesso alla whitelist.)


0

Supponendo che la tua shell bash sia extglob shoptimpostata su on, ecco un'alternativa un po 'più conservativa:

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

(... che accompagna il suggerimento comm eccellente di @ gardenhead altrimenti!)


0

A meno che l'output di non ls /home/me/myfolder2tocleanup/superi il limite massimo dell'argomento shell ARG_MAX che è di circa 2 MB per Ubuntu, suggerirei quanto segue.


Un'implementazione del comando a una riga che farà il lavoro sarebbe la seguente:

  1. Copia il dont-delete.txtfile nella directory contenente i file da eliminare in questo modo:
cp dont-delete.txt /home/me/myfolder2tocleanup/
  1. cd nella directory contenente i file da eliminare in questo modo:
cd /home/me/myfolder2tocleanup/
  1. Esegui una prova a secco per testare il comando e farlo stampare i nomi dei file che rileva come da eliminare senza eliminarli effettivamente, in questo modo:
ls -p | grep -v / | sed 's/\<dont-delete.txt\>//g' | sort | comm -3 - <(sort dont-delete.txt) | xargs echo | tr " " "\n"
  1. Se sei soddisfatto dell'output, elimina i file eseguendo il comando in questo modo:
ls -p | grep -v / | sed 's/\<dont-delete.txt\>//g' | sort | comm -3 - <(sort dont-delete.txt) | xargs rm

spiegazione:

  • ls -pelencherà tutti i file e le directory nella directory corrente e l'opzione -paggiungerà /a ai nomi delle directory.
  • grep -v /escluderà le directory rimuovendo tutti gli elementi che contengono a /nei loro nomi.
  • sed 's/\<dont-delete.txt\>//g'escluderà il dont-delete.txtfile, quindi non verrà eliminato nel processo.
  • sortsarà, solo per essere sicuro, ordinare l'output rimanente di ls.
  • comm -3 - <(sort dont-delete.txt)ordina il dont-delete.txtfile, lo confronta con l'output ordinato di lsed esclude i nomi di file che esistono in entrambi.
  • xargs rmrimuoverà tutti i nomi di file rimanenti nell'output già elaborato di ls. Ciò significa che tutti gli elementi nella directory corrente verranno rimossi tranne le directory , i file elencati nel dont-delete.txtfile e il dont-delete.txtfile stesso

Nella parte a secco:

  • xargs echo stamperà i file che dovrebbero essere rimossi.
  • tr " " "\n" tradurrà gli spazi in nuove righe per facilitare la leggibilità.

0

Consiglio vivamente di utilizzare la rsyncsoluzione pubblicata qui ; altrimenti utilizzare la soluzione di seguito con una condizione eccezionale menzionata.

Supponendo che non ci siano spazi bianchi (Spaces / Tabs) nei tuoi file "elencati in un file chiamato excludelist, allora dovresti fare:

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

Basta aggiungere -deleteal comando sopra per eliminare i file che non esistono nel file exclelist . Se il ritrovamento non ha -deletel'opzione è possibile utilizzare rmcon -execcome segue:

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

O usando-exec+ invece con terminator .

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

echo viene utilizzato solo per il funzionamento a secco.


-1

Il mio consiglio è:

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

Aggiornamento 07-08-2018

Esempio:

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

Nota dopo la riga 3 avrai il dont-delete.txtfile con i contenuti:

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

(il leader ./è molto importante )

I file ce dverranno eliminati.


Ho provato questo con un file di testo con i nomi dei file separati da una nuova riga. Ha finito per eliminare tutti i file nella directory.
Jacques MALAPRADE,

Immagino che il tuo "elenco di conservazione" fosse sbagliato.
nyxz,

Ho aggiunto un esempio di utilizzo.
nyxz,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.