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.

Aleksandra
źródło
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
nr
źródło
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ś.

aks
źródło
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.

hek2mgl
źródło
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
Promień
źródło
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.

tgdavies
źródło
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.

Mark Drago
źródło
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

Evgeni Sergeev
źródło
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.

Uzayr
źródło
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 )
Quentin
źródło
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
nthparameter
źródło
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
Chand Priyankara
źródło
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
Ferroao
źródło
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.

Marco
źródło