Jak usunąć wszystkie oprócz 10 najnowszych plików w systemie Linux?
49
Staram się, aby katalog pełen plików dziennika był zarządzalny. Co noc chcę usunąć wszystkie oprócz 10 najnowszych. Jak mogę to zrobić za pomocą jednego polecenia?
@ Michael Jak mój może być duplikatem tego, jeśli moja dyskusja została utworzona jako pierwsza?
dev4life
@ovaherenow „twój”? Nie widzę twojego imienia w żadnym z pytań, więc nie jestem pewien, co masz na myśli. Mój komentarz nawet nie wskazuje, który jest duplikatem drugiego. Ale to nie ma znaczenia - to tylko komentarz z linkiem. Można go dodać do jednego lub obu pytań. To do kogoś innego - administratora - oznaczenie jednego lub drugiego jako duplikatu. Żaden nikczemny sztuczka nie był zamierzony i nie należy go postrzegać. Ważniejsze od tego, które pytanie było pierwsze, jest to, które pytanie ma najlepszą odpowiedź - ponownie link ułatwia tę dyskusję, nie określa odpowiedzi.
Michael
@michael wow. To naprawdę dziwne. Ten wątek został przeze mnie otwarty, ale oczywiście nie jest to moja nazwa użytkownika. Ale dostaję powiadomienie o tym wątku.
dev4life
Odpowiedzi:
58
Aby uzyskać przenośne i niezawodne rozwiązanie, spróbuj tego:
ls -1tr | head -n -10 | xargs -d '\n' rm -f --
tail -n -10Składni w jednej z pozostałych odpowiedzi nie wydają się działać wszędzie (czyli nie na moich systemów RHEL5).
I za pomocą $()lub ``w linii poleceń rmuruchamia ryzyko
dzielenie nazw plików za pomocą białych znaków i
przekroczenie maksymalnego limitu znaków w linii poleceń.
xargsnaprawia oba te problemy, ponieważ automatycznie obliczy liczbę argumentów, które może przekazać w ramach limitu znaków, a wraz z -d '\n'nim podzieli się tylko na granicy linii wejściowej. Technicznie może to nadal powodować problemy z nazwami plików z nową linią, ale jest to o wiele mniej powszechne niż nazwy plików ze spacjami, a jedynym sposobem obejścia nowych linii byłoby znacznie bardziej skomplikowane, prawdopodobnie obejmujące przynajmniej awk, jeśli nie perl.
Jeśli nie masz xargs (może stare systemy AIX?), Możesz zrobić z niego pętlę:
ls -1tr | head -n -10 | while IFS= read -r f; do
rm -f "$f"
done
Będzie to nieco wolniejsze, ponieważ odradza się osobno rmdla każdego pliku, ale nadal będzie unikać ostrzeżeń 1 i 2 powyżej (ale nadal cierpi na nowe wiersze w nazwach plików).
@HaukeLaging: Ze strony podręcznika head(1):-n, --lines=[-]K print the first K lines instead of the first 10; with the leading '-', print all but the last K lines of each file
Isaac Freeman
W systemie Solaris 10 headi xargspodawaj błędy. Prawidłowe opcje to head -n 10i xargs rm -f. Pełne polecenie tols -1tr ./ | head -n 10 | xargs rm -rf
Dmitrij Sandałow
1
Zauważ, że ls -1trto liczba JEDEN, a nie litera „L”.
shonky użytkownik Linuxa
1
Należy również zauważyć, że znaki nowej linii to rzadkie, ale prawidłowe znaki w nazwach plików ext. Oznacza to, że jeśli masz pliki „to” i „tamto”, jeśli ten skrypt trafi „tamto”, usunie „tamto”, błąd, gdy nie będzie można usunąć „rzecz”, i odejdzie „to \ nthing” (zamierzony cel) pozostaje nienaruszone.
Synthead,
1
@IsaacFreeman Usunąłem złą radę, na zdrowie.
Walf
20
Kod, który chcesz dołączyć do skryptu, to
rm -f $(ls -1t /path/to/your/logs/ | tail -n +11)
Opcja -1(numeryczna) drukuje każdy plik w jednym wierszu, aby być bezpiecznym. -fOpcja rmmówi, że ignorowanie nieistniejących plików do kiedy lsnic nie zwraca.
Oryginalny plakat tutaj. Próbowałem tego polecenia, ale to nie działa - pojawia się błąd „rm: missing operand”
user75814
2
Czy użyłeś właściwej ścieżki? Oczywiście /path/to/your/logsjest to po prostu nadrobione, abyś podał prawidłową ścieżkę. Aby znaleźć błąd wykonania części ls -t /path/...i ls -t /path/... | tail -n +11sam i sprawdzić, czy wynik jest prawidłowy.
Daniel Böhmer,
1
@HaukeLaging Jestem pewien, że coś jest nie tak. Uważaj +przed liczbą. Powoduje to, że tailnie drukuje ostatnich n elementów, ale wszystkie elementy zaczynające się od n-tego. Zalogowałem się ponownie i polecenie zdecydowanie powinno usunąć we wszystkich okolicznościach tylko elementy starsze niż 10 najnowszych plików.
Daniel Böhmer
@halo Rzeczywiście przepraszam.
Hauke Laging
2
Myślę, że twoja odpowiedź jest bardzo niebezpieczna, lswypisuje nazwę pliku, a nie ścieżkę, więc rm -fspróbujesz usunąć pliki z bieżącego katalogu
-mtime 10 -deleteusuwa tylko pliki zmodyfikowane dokładnie 10 dni temu. Starsze pliki nie zostaną usunięte. -mtime +11 -deleteusunie pliki, które zostały zmodyfikowane 11 lub więcej dni temu, pozostawiając ostatnie 10 dni plików dziennika.
Markus
1
Myślę, że użycie %pzamiast zamiast %Pjest potrzebne, printfaby pliki miały pełną ścieżkę. W przeciwnym razie rm -rfnie działa.
jalopaba
9
Narzędzie takie jak logrotate robi to za Ciebie. To znacznie ułatwia zarządzanie logami. Możesz także dołączyć dodatkowe procedury czyszczenia, takie jak przesycenie halo.
#! /bin/sh
# keepnewest
#
# Simple directory trimming tool to handle housekeeping
# Scans a directory and deletes all but the N newest files
#
# Usage: cleanup <dir> <number of files to keep>
#
# v 1.0 Piers Goodhew 1/mar/2007. No rights retained.
if [ $# -ne 2 ]; then
echo 1>&2 "Usage: $0 <dir> <number of files to keep>"
exit 1
fi
cd $1
files_in_dir=`ls | wc -l`
files_to_delete=`expr $files_in_dir - $2`
if [ $files_to_delete -gt 0 ]; then
ls -t | tail -n $files_to_delete | xargs rm
if [ $? -ne 0 ]; then
echo "An error ocurred deleting the files"
exit 1
else
echo "$files_to_delete file(s) deleted."
fi
else
echo "nothing to delete!"
fi
Dziękujemy za udzielenie tej odpowiedzi. Podanie takiego kodu jest pomocne, ale pomaga również dostarczyć dodatkowe informacje o tym, jak można go użyć lub co robi kod. Możesz użyć przycisku edycji , aby w przyszłości ulepszyć swój post.
nhinkle
1
W Bash możesz spróbować:
ls -1t | (i=0; while read f; do
if [ $i -lt 10 ]; then
((i++))
continue
else
rm -f "$f"
fi
done)
Pomija 10 najnowszych als usuwa resztę. logrotate może być lepszy, chcę tylko poprawić nieprawidłowe odpowiedzi dotyczące powłoki.
nie jestem pewien, że to pomoże każdemu, ale aby uniknąć potencjalnych problemów z dziwacznymi postaciami, których użyłem lsdo sortowania, ale potem użyłem plików inodedo usunięcia.
W bieżącym katalogu zachowuje 10 najnowszych plików na podstawie czasu modyfikacji.
Potrzebowałem eleganckiego rozwiązania dla busyboksa (routera), wszystkie xargi lub rozwiązania macierzowe były dla mnie bezużyteczne - nie ma tam takiego polecenia. find i mtime nie jest właściwą odpowiedzią, ponieważ mówimy o 10 elementach i niekoniecznie o 10 dniach. Odpowiedź Espo była najkrótsza, najczystsza i prawdopodobnie najbardziej niewersalna.
Błąd spacji i brak usuwania plików są po prostu rozwiązywane w standardowy sposób:
rm "$(ls -td *.tar | awk 'NR>7')" 2>&-
Trochę więcej wersji edukacyjnej: możemy to wszystko zrobić, jeśli korzystamy z awk inaczej. Zwykle używam tej metody do przekazywania (zwracania) zmiennych z awk do sh. Kiedy cały czas czytamy, że nie da się tego zrobić, zaczynam się różnić: oto metoda.
Przykład plików .tar bez problemu dotyczących spacji w nazwie pliku. Aby przetestować, zamień „rm” na „ls”.
W przypadku użycia ls -tpolecenia nie wyrządzi żadnej szkody na tak głupich przykładach jak: touch 'foo " bar'i touch 'hello * world'. Nie żebyśmy kiedykolwiek tworzyli pliki o takich nazwach w prawdziwym życiu!
Dygresja. Gdybyśmy chcieli przekazać zmienną do sh w ten sposób, po prostu zmodyfikowalibyśmy wydruk:
print "VarName="$1
ustawić zmienną VarNamena wartość $1. Za jednym razem można utworzyć wiele zmiennych. To VarNamestaje się normalna zmienna sh i można go używać w skrypcie lub muszli później. Tak więc, aby utworzyć zmienne za pomocą awk i zwrócić je powłoce:
Odpowiedzi:
Aby uzyskać przenośne i niezawodne rozwiązanie, spróbuj tego:
tail -n -10
Składni w jednej z pozostałych odpowiedzi nie wydają się działać wszędzie (czyli nie na moich systemów RHEL5).I za pomocą
$()
lub``
w linii poleceńrm
uruchamia ryzykoxargs
naprawia oba te problemy, ponieważ automatycznie obliczy liczbę argumentów, które może przekazać w ramach limitu znaków, a wraz z-d '\n'
nim podzieli się tylko na granicy linii wejściowej. Technicznie może to nadal powodować problemy z nazwami plików z nową linią, ale jest to o wiele mniej powszechne niż nazwy plików ze spacjami, a jedynym sposobem obejścia nowych linii byłoby znacznie bardziej skomplikowane, prawdopodobnie obejmujące przynajmniej awk, jeśli nie perl.Jeśli nie masz xargs (może stare systemy AIX?), Możesz zrobić z niego pętlę:
Będzie to nieco wolniejsze, ponieważ odradza się osobno
rm
dla każdego pliku, ale nadal będzie unikać ostrzeżeń 1 i 2 powyżej (ale nadal cierpi na nowe wiersze w nazwach plików).źródło
head(1)
:-n, --lines=[-]K print the first K lines instead of the first 10; with the leading '-', print all but the last K lines of each file
head
ixargs
podawaj błędy. Prawidłowe opcje tohead -n 10
ixargs rm -f
. Pełne polecenie tols -1tr ./ | head -n 10 | xargs rm -rf
ls -1tr
to liczba JEDEN, a nie litera „L”.Kod, który chcesz dołączyć do skryptu, to
Opcja
-1
(numeryczna) drukuje każdy plik w jednym wierszu, aby być bezpiecznym.-f
Opcjarm
mówi, że ignorowanie nieistniejących plików do kiedyls
nic nie zwraca.źródło
/path/to/your/logs
jest to po prostu nadrobione, abyś podał prawidłową ścieżkę. Aby znaleźć błąd wykonania częścils -t /path/...
ils -t /path/... | tail -n +11
sam i sprawdzić, czy wynik jest prawidłowy.+
przed liczbą. Powoduje to, żetail
nie drukuje ostatnich n elementów, ale wszystkie elementy zaczynające się od n-tego. Zalogowałem się ponownie i polecenie zdecydowanie powinno usunąć we wszystkich okolicznościach tylko elementy starsze niż 10 najnowszych plików.ls
wypisuje nazwę pliku, a nie ścieżkę, więcrm -f
spróbujesz usunąć pliki z bieżącego kataloguNajwyraźniej parsowanie
ls
jest złe .Jeśli każdy plik jest tworzony codziennie i chcesz zachować pliki utworzone w ciągu ostatnich 10 dni, możesz:
Lub jeśli każdy plik jest tworzony arbitralnie:
źródło
-mtime 10 -delete
usuwa tylko pliki zmodyfikowane dokładnie 10 dni temu. Starsze pliki nie zostaną usunięte.-mtime +11 -delete
usunie pliki, które zostały zmodyfikowane 11 lub więcej dni temu, pozostawiając ostatnie 10 dni plików dziennika.%p
zamiast zamiast%P
jest potrzebne,printf
aby pliki miały pełną ścieżkę. W przeciwnym razierm -rf
nie działa.Narzędzie takie jak logrotate robi to za Ciebie. To znacznie ułatwia zarządzanie logami. Możesz także dołączyć dodatkowe procedury czyszczenia, takie jak przesycenie halo.
źródło
Trochę zmodyfikowałem podejście Izaaka .
Teraz działa z wybraną ścieżką:
źródło
Od tutaj :
źródło
W Bash możesz spróbować:
Pomija 10 najnowszych als usuwa resztę. logrotate może być lepszy, chcę tylko poprawić nieprawidłowe odpowiedzi dotyczące powłoki.
źródło
nie jestem pewien, że to pomoże każdemu, ale aby uniknąć potencjalnych problemów z dziwacznymi postaciami, których użyłem
ls
do sortowania, ale potem użyłem plikówinode
do usunięcia.W bieżącym katalogu zachowuje 10 najnowszych plików na podstawie czasu modyfikacji.
ls -1tri | awk '{print $1}' | head -n -10 | xargs -n1 -t -Iinode find . -inum inode -exec rm -i {} \;
źródło
Potrzebowałem eleganckiego rozwiązania dla busyboksa (routera), wszystkie xargi lub rozwiązania macierzowe były dla mnie bezużyteczne - nie ma tam takiego polecenia. find i mtime nie jest właściwą odpowiedzią, ponieważ mówimy o 10 elementach i niekoniecznie o 10 dniach. Odpowiedź Espo była najkrótsza, najczystsza i prawdopodobnie najbardziej niewersalna.
Błąd spacji i brak usuwania plików są po prostu rozwiązywane w standardowy sposób:
Trochę więcej wersji edukacyjnej: możemy to wszystko zrobić, jeśli korzystamy z awk inaczej. Zwykle używam tej metody do przekazywania (zwracania) zmiennych z awk do sh. Kiedy cały czas czytamy, że nie da się tego zrobić, zaczynam się różnić: oto metoda.
Przykład plików .tar bez problemu dotyczących spacji w nazwie pliku. Aby przetestować, zamień „rm” na „ls”.
Wyjaśnienie:
ls -td *.tar
wyświetla wszystkie pliki .tar posortowane według czasu. Aby zastosować do wszystkich plików w bieżącym folderze, usuń część „d * .tar”awk 'NR>7...
pomija pierwsze 7 liniiprint "rm \"" $0 "\""
konstruuje linię: rm „nazwa pliku”eval
wykonuje toPonieważ używamy
rm
, nie użyłbym powyższego polecenia w skrypcie! Mądrzejsze użycie to:W przypadku użycia
ls -t
polecenia nie wyrządzi żadnej szkody na tak głupich przykładach jak:touch 'foo " bar'
itouch 'hello * world'
. Nie żebyśmy kiedykolwiek tworzyli pliki o takich nazwach w prawdziwym życiu!Dygresja. Gdybyśmy chcieli przekazać zmienną do sh w ten sposób, po prostu zmodyfikowalibyśmy wydruk:
ustawić zmienną
VarName
na wartość$1
. Za jednym razem można utworzyć wiele zmiennych. ToVarName
staje się normalna zmienna sh i można go używać w skrypcie lub muszli później. Tak więc, aby utworzyć zmienne za pomocą awk i zwrócić je powłoce:źródło
Zgadzam się, że parsowanie ls jest złe!
Upewnij się, że tylko 5 ostatnich plików jest przechowywanych na
/srv/backups
ścieżce.źródło
Na podstawie odpowiedzi Izaaka Freemana, ale działa na dowolnym katalogu:
Możesz także ustawić prefiks, np
$PATH_TO_DELETE/testFi*
Jeśli użyjesz
*
poPATH
ls
to wypisze bezwzględne nazwy plików, przynajmniej w „moim” bashu :)źródło