Usuń wszystkie pliki / katalogi oprócz jednego pliku

243

Mam katalog zawierający dużą liczbę plików. Chcę usunąć wszystkie pliki oprócz pliku file.txt. Jak mam to zrobic?

Istnieje zbyt wiele plików, aby usunąć osobno niechciane pliki, a ich nazwy są zbyt różnorodne, aby używać *, aby usunąć je wszystkie oprócz tego jednego pliku.

Ktoś zasugerował użycie

rm !(file.txt)

Ale to nie działa. Zwraca:

Badly placed ()'s 

Mój system operacyjny to Scientific Linux 6.

Jakieś pomysły?

Kantura
źródło
13
przenieść ten, który chcesz zachować, a następnie pozostałe?
Olivier Dulac,
5
@OlivierDulac Czy jesteśmy jedynymi osobami w tym pytaniu, które nie zastanawiają się nad pytaniem?
Shadur
1
@shadur: no cóż, mogę się z nimi odnosić: oneliniści są atrakcyjni ... ^^
Olivier Dulac
1
Dzięki i tak, szukałem rozwiązania jednowierszowego. Przenoszenie plików jest zbyt czasochłonne, ponieważ muszę to robić dość często.
Kantura

Odpowiedzi:

287

POSIXly:

find . ! -name 'file.txt' -type f -exec rm -f {} +

usunie wszystkie zwykłe pliki (rekurencyjnie, w tym ukryte) oprócz file.txt. Aby usunąć katalogi, zmienić -type fsię -type di dodaj -ropcję rm.

W bash, aby użyć rm -- !(file.txt), musisz włączyć extglob :

$ shopt -s extglob 
$ rm -- !(file.txt)

(lub dzwoniąc bash -O extglob)

Zauważ, że extglobdziała tylko w bashrodzinie powłok Korna. A używanie rm -- !(file.txt)może powodować Argument list too longbłąd.

W zshmożna użyć ^do zanegowania wzorca przy włączonym rozszerzonymglobie :

$ setopt extendedglob
$ rm -- ^file.txt

lub korzystając z tej samej składni kshi bashopcje ksh_globi no_bare_glob_qualwłączona.

Cuonglm
źródło
9
Określanie katalogu to dobra praktyka (w tym przypadku pełna ścieżka? Lub może dodać tutaj ostrzeżenie, że to polecenie usuwa każdy plik zaczynający się od bieżącego katalogu roboczego ?). Zazwyczaj piszę też każdy przykład z „ rmas” echo rmi proszę ludzi, aby wydobywali echo tylko wtedy, gdy są naprawdę pewni, że zrobi to, co chcą. Poza tym +1 za dokładną odpowiedź
Olivier Dulac
11
Proponuję -deletezamiast -exec, krótszego i łatwiejszego do zapamiętania
Izkata
6
@Izkata: -deletenie jest zdefiniowany przez POSIX.
cuonglm
5
Jak wykluczyć listę plików?
Meysam
5
@Meysam - zobacz moją odpowiedź na rozwiązanie, które obsłuży listę plików. Jeszcze z findrozwiązaniem Gnouc, które możesz zrobić ! \( -name one_file -o -name two_file \)i tak dalej.
mikeserv
112

Kolejne podejście w innym kierunku (iff nie ma spacji w nazwach plików)

ls | grep -v file.txt | xargs rm

lub (działa, nawet jeśli w nazwach plików są spacje)

ls | grep -v file.txt | parallel rm

z man grep:

 -v, --invert-match
          Invert the sense of matching, to select non-matching lines.  (-v is specified by POSIX)
Sebastian
źródło
2
Nie zadziała, jeśli w nazwach plików jest spacja ...
Matteo
1
Ciao @ Matteo, działa również dla plików ze spacjami, ale musisz otoczyć wzór grep cudzysłowami, np ls | grep -v "a file with spaces.bin" | xargs rm. To jest normalna grepskładnia.
Sebastian
1
@Sebastian Problemem nie jest grepale rm. rmotrzyma listę argumentów oddzielonych spacjami. Spróbuj touch 'a b'; touch 'c d'; ls | grep -v 'a b' | xargs rm: dostaniesz rm: c: No such file or directoryirm: d: No such file or directory
Matteo
1
Tak, to jest powszechny problem. Zobacz opcje -print0w findi -0w xargs. Oto obejście, ale jest nieco niewygodne. find . -maxdepth 1 -type f | grep -v 'a b' | tr '\n' '\0' | xargs -0 rm. Nawiasem mówiąc, GNU paralleldobrze sobie z tym radzi (prawie zawsze używam go jako zamiennika xargs:ls | grep -v 'a b' | parallel rm
Sebastian
1
To nie tylko spacje, wszystkie spacje i znaki nowej linii, ale także cytowanie znaków (pojedyncze, podwójne cudzysłowy i ukośnik odwrotny) oraz nazwy plików zaczynające się od -.
Stéphane Chazelas,
32

Zachowaj kopię, usuń wszystko, przywróć kopię:

{   rm -rf *
    tar -x
} <<TAR
$(tar -c $one_file)
TAR

W jednej linii:

{ rm -rf *; tar -x; } <<< $(tar -c $one_file)

Ale to wymaga powłoki, która obsługuje ciągi tutaj.

mikeserv
źródło
10
To jest nieco oszałamiające.
vschum
2
@Derek - zobacz edycję.
mikeserv
2
Ale jeśli zostanie to przerwane w połowie lub jeśli coś pójdzie nie tak, plik zniknie.
kasperd
2
@kasperd - nie. Tylko jeśli powłoka nadrzędna umiera między czasem jej uruchomienia rma tar -x. rmmoże zawieść tyle, ile chce.
mikeserv
2
Czy przeniesienie go do innego katalogu i przeniesienie go z powrotem nie jest bardziej wydajne? Nie musimy zajmować się zawartością pliku, a jedynie jego ścieżką.
leonbloy
23

wszyscy nad tym zastanawiacie się.

cd ..
mv fulldir/file.txt /tmp/
rm -rf fulldir
mkdir fulldir
mv /tmp/file.txt fulldir/

Gotowy.

EDYCJA Właściwie łatwiej:

cd ..
ln fulldir/file.txt ./
rm -rf fulldir
mkdir -p fulldir
mv file.txt fulldir/
Shadur
źródło
3
to samo robi moja odpowiedź. exce [pt nie traci żadnych uprawnień w reż.
mikeserv
6
Jeśli jest to duży plik w innym systemie plików od / tmp i próbujesz usunąć wszystko z katalogu głównego systemu plików w dół, wówczas przeniesienie go w bezpieczne miejsce może nie być możliwe.
Johnny,
1
To zdecydowanie najprostsza odpowiedź.
Ben Liyanage
17

W moim systemie operacyjnym Scientific Linux 6 działa to:

shopt -s extglob
rm !(file.txt)

Mam również Debian 32-bitowy zainstalowany na maszynie wirtualnej. Powyższe nie działa, ale następujące czynności:

find . -type f ! -name 'file.txt' -delete
Kantura
źródło
1
Masz na myśli findrozwiązanie, które podałem, nie zadziałało?
cuonglm
„nie działał” lub „nie działa”. Tak, twoje rozwiązanie wyszukiwania również działa. Dzięki.
Kantura
1
Czy możesz to zrobić więcej szczegółów? Jak „nie działało? Nie usuwa innych plików, ani nie usuwa, file.txtani nic innego?
cuonglm,
Miałem na myśli, że w systemie Debian „$ rm! (File.txt)” zwraca „Źle umieszczone () 's'. Próbowałem więc „$ shopt -s extglob”, ale zwróciło się: „shopt: Nie znaleziono polecenia”. Potem wypróbowałem rozwiązanie „znajdź”. To działa.
Kantura
1
Och, więc oczywiście to nie działa. shoptnie jest tcshwbudowany.
cuonglm
10

Użyj rm !("file.txt")zamiastrm !(file.txt)

www.wishinfo.net
źródło
4
Nie ma to absolutnie żadnej różnicy. Problem polegał na tym, że OP 1) nie używał powłoki obsługującej ten format i 2) nawet w bash, musisz ją włączyć shopt -s extglob. W każdym razie cytowanie takiej prostej nazwy pliku nie miałoby znaczenia.
terdon
1
Aby zachować folder utils, zrób - rm -rf! („Utils”)
James Jithin
7

Aby dać inną odpowiedź, możesz użyć domyślnego zachowania, rmktóre nie spowoduje usunięcia folderów:

mkdir tmp && mv file.txt tmp  # create tmp dir and move files there
rm                            # delete all other files
mv tmp/* . && rm -rf tmp      # move all files back and delete tmp dir
Nithin
źródło
1

Uważam, że to podejście jest bardzo proste, działa i nie wymaga żadnych specjalnych rozszerzeń (o których wiem!)

ls --hide=file.txt | xargs rm
2 bity
źródło