Unix: jak usunąć pliki wymienione w pliku


92

Mam długi plik tekstowy z listą masek plików, które chcę usunąć

Przykład:

/tmp/aaa.jpg
/var/www1/*
/var/www/qwerty.php

Muszę je usunąć. Wypróbowano rm `cat 1.txt` i stwierdzono, że lista jest za długa.

Znalazłem to polecenie, ale kiedy sprawdzam foldery z listy, niektóre z nich nadal mają pliki. xargs rm <1.txtRęczne wywołanie rm usuwa pliki z takich folderów, więc nie ma problemu z uprawnieniami.


8
Chociaż minęło sześć lat: czy mógłbyś zaakceptować jedną z odpowiedzi? Oznaczy to pytanie jako rozwiązane i pomoże również innym użytkownikom.
MERose

Odpowiedzi:


114

Nie jest to zbyt wydajne, ale zadziała, jeśli potrzebujesz wzorców glob (jak w / var / www / *)

for f in $(cat 1.txt) ; do 
  rm "$f"
done

Jeśli nie masz żadnych wzorców i masz pewność, że ścieżki w pliku nie zawierają białych znaków ani innych dziwnych rzeczy, możesz użyć xargs w następujący sposób:

xargs rm < 1.txt

4
Co się stanie z pierwszym rozwiązaniem (przy użyciu CAT), jeśli ścieżka zawiera spację?
Tytoń

58

Zakładając, że lista plików znajduje się w pliku 1.txt, wykonaj:

xargs rm -r <1.txt

Ta -ropcja powoduje rekursję do wszystkich katalogów wymienionych w 1.txt.

Jeśli jakieś pliki są tylko do odczytu, użyj -fopcji wymuszenia usunięcia:

xargs rm -rf <1.txt

Zachowaj ostrożność podczas wprowadzania danych do dowolnego narzędzia, które wykonuje programowe usuwanie. Zrobić pewien , że pliki wymienione w pliku wejściowym są naprawdę ma być usunięty. Uważaj szczególnie na pozornie proste literówki. Na przykład, jeśli wprowadzisz spację między plikiem a jego sufiksem, pojawią się dwie oddzielne nazwy plików:

file .txt

to właściwie dwa oddzielne pliki: filei .txt.

To może nie wydawać się takie niebezpieczne, ale jeśli literówka wygląda mniej więcej tak:

myoldfiles *

Wtedy zamiast usuwając wszystkie pliki, które zaczynają się myoldfiles, będziesz skończyć usunięciem myoldfilesi wszystkie nie-dot-plików i katalogów w bieżącym katalogu. Prawdopodobnie nie to, czego chciałeś.


1
"myoldfiles /" lub "/ tmp" nawet bardziej katastrofalne w przypadku -rf. Zwróć uwagę na spację obok „/”.
Ray

23

Użyj tego:

while IFS= read -r file ; do rm -- "$file" ; done < delete.list

Jeśli potrzebujesz ekspansji glob, możesz pominąć cytowanie $file:

IFS=""
while read -r file ; do rm -- $file ; done < delete.list

Ostrzegamy jednak, że nazwy plików mogą zawierać „problematyczne” treści i użyłbym wersji bez cytowania. Wyobraź sobie ten wzór w pliku

*
*/*
*/*/*

Spowoduje to usunięcie całkiem sporo z bieżącego katalogu! Zachęcam cię do przygotowania listy usuwania w taki sposób, że wzorce glob nie są już wymagane, a następnie użyj cytowania, jak w moim pierwszym przykładzie.


3
Jest to obecnie jedyna odpowiedź, która obsługuje wszystkie /My Dir With Spaces/ /Spaces and globs/*.txt, --file-with-leading-dashesi jako bonus To POSIX i działa na systemie GNU / Linux, MacOS i BSD.
ten drugi facet

17

Możesz użyć „\ n” do zdefiniowania znaku nowego wiersza jako separatora.

xargs -d '\n' rm < 1.txt

Uważaj na -rf, ponieważ może usunąć to, czego nie chcesz, jeśli 1.txt zawiera ścieżki ze spacjami. Dlatego nowy ogranicznik linii jest nieco bezpieczniejszy.

W systemach BSD można użyć opcji -0, aby użyć znaków nowej linii jako separatora, jak poniżej:

xargs -0 rm < 1.txt

1
Na OS X to mnie dostajexargs: illegal option -- d
waldyrious

Słuszna uwaga. Wygląda na to, że nie ma innej opcji niż -0na OS X.
Ray

2
xargs -I_ rm _działa również w OS X :) patrz stackoverflow.com/a/39335402/266309
waldyrious

Jeśli używasz xargs BSD (gdzie nie ma -d), możesz najpierw przekonwertować tr '\n' '\0' < 1.txt | xargs -0 rm
znaki

14

Możesz użyć tej jednej linijki:

cat 1.txt | xargs echo rm | sh

Który powoduje rozszerzenie powłoki, ale wykonuje rmminimalną liczbę razy.


O ile jakieś rozszerzenie nie jest zbyt długie?
Douglas Leeder

1
To prawda, że ​​glob może wygenerować zbyt długą listę argumentów - możesz dodać -n <n>argument, aby xargszmniejszyć liczbę argumentów przekazywanych do każdego rm, ale to nadal nie ochroni cię przed pojedynczym globem, który przekracza limit.
tgdavies

13

xargs -I{} sh -c 'rm {}' < 1.txtpowinieneś robić, co chcesz. Uważaj na to polecenie, ponieważ jeden nieprawidłowy wpis w tym pliku może spowodować wiele problemów.

Ta odpowiedź została zredagowana po tym, jak @tdavies wskazał, że oryginał nie rozwijał powłoki.


Dzięki, ale nie trzeba usuwać folderów, tylko pliki
Alexander,

2
Kulki nie zostaną rozszerzone, ponieważ powłoka nigdy ich nie „widzi”.
tgdavies

@tdavies wow - masz rację. To polecenie (choć trochę brzydsze) zadziała:xargs -I{} sh -c 'rm {}' < 1.txt
Mark Drago

4

W tym konkretnym przypadku, ze względu na niebezpieczeństwa przytoczone w innych odpowiedziach, zrobiłbym to

  1. Edycja w np. Vimie i :%s/\s/\\\0/g, przed wszystkimi znakami spacji z ukośnikiem odwrotnym.

  2. Następnie :%s/^/rm -rf /poprzedzając polecenie. Dzięki temu -rnie musisz się martwić, że katalogi są wymienione po plikach w nich zawartych, a dzięki -ftemu nie będziesz narzekać z powodu brakujących plików lub zduplikowanych wpisów.

  3. Uruchom wszystkie polecenia: $ source 1.txt


Spacje to nie jedyne znaki, które wymagają ucieczki, popularne w nazwach plików są również wszelkiego rodzaju nawiasy, które mogą prowadzić do niechcianego rozwinięcia.
emk2203

4

cat 1.txt | xargs rm -f | bash Uruchomienie polecenia wykona następujące czynności tylko dla plików.

cat 1.txt | xargs rm -rf | bash Uruchomienie polecenia spowoduje następujące rekurencyjne zachowanie.


2

Aby zapewnić inny sposób, możesz również po prostu użyć następującego polecenia

$ cat to_remove
/tmp/file1
/tmp/file2
/tmp/file3
$ rm $( cat to_remove )

1

Oto kolejny przykład zapętlenia. Ten zawiera również „instrukcję if” jako przykład sprawdzania, czy wpis jest „plikiem” (lub na przykład „katalogiem”):

for f in $(cat 1.txt); do if [ -f $f ]; then rm $f; fi; done

0

Tutaj można użyć zestawu folderów z deletelist.txt natomiast unikanie pewnych wzorców , jak również

foreach f (cat deletelist.txt)
    rm -rf ls | egrep -v "needthisfile|*.cpp|*.h"
end

0

Dzięki temu nazwy plików będą miały spacje (przykład odtwarzalny).

# Select files of interest, here, only text files for ex.
find -type f -exec file {} \; > findresult.txt
grep ": ASCII text$" findresult.txt > textfiles.txt
# leave only the path to the file removing suffix and prefix
sed -i -e 's/:.*$//' textfiles.txt
sed -i -e 's/\.\///' textfiles.txt

#write a script that deletes the files in textfiles.txt
IFS_backup=$IFS
IFS=$(echo "\n\b")
for f in $(cat textfiles.txt); 
do 
rm "$f"; 
done
IFS=$IFS_backup

# save script as "some.sh" and run: sh some.sh

0

W przypadku, gdy ktoś woli sedi usuwa bez rozszerzenia symboli wieloznacznych :

sed -e "s/^\(.*\)$/rm -f -- \'\1\'/" deletelist.txt | /bin/sh

Przypomnienie: użyj bezwzględnych ścieżek dostępu do pliku lub upewnij się, że jesteś we właściwym katalogu.

A dla kompletności to samo z awk:

awk '{printf "rm -f -- '\''%s'\''\n",$1}' deletelist.txt | /bin/sh

Rozszerzanie symboli wieloznacznych zadziała, jeśli zostaną usunięte pojedyncze cudzysłowy, ale jest to niebezpieczne w przypadku, gdy nazwa pliku zawiera spacje. Wymagałoby to dodania cudzysłowów wokół symboli wieloznacznych.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.