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?
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?
Odpowiedzi:
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
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).
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
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
ls -1trto liczba JEDEN, a nie litera „L”.
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.
/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.
+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.
lswypisuje nazwę pliku, a nie ścieżkę, więc rm -fspróbujesz usunąć pliki z bieżącego katalogu
Najwyraźniej parsowanie lsjest złe .
Jeśli każdy plik jest tworzony codziennie i chcesz zachować pliki utworzone w ciągu ostatnich 10 dni, możesz:
find /path/to/files -mtime 10 -delete
Lub jeśli każdy plik jest tworzony arbitralnie:
find /path/to/files -maxdepth 1 -type f -printf '%Ts\t%P\n' | sort -n | head -n -10 | cut -f 2- | xargs rm -rf
-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.
%pzamiast zamiast %Pjest potrzebne, printfaby pliki miały pełną ścieżkę. W przeciwnym razie rm -rfnie działa.
Trochę zmodyfikowałem podejście Izaaka .
Teraz działa z wybraną ścieżką:
ls -d -1tr /path/to/folder/* | head -n -10 | xargs -d '\n' rm -f
Od tutaj :
#! /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
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.
ls -1tri | awk '{print $1}' | head -n -10 | xargs -n1 -t -Iinode find . -inum inode -exec rm -i {} \;
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”.
eval $(ls -td *.tar | awk 'NR>7 { print "rm \"" $0 "\""}')
Wyjaśnienie:
ls -td *.tarwyś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 linii
print "rm \"" $0 "\"" konstruuje linię: rm „nazwa pliku”
eval wykonuje to
Ponieważ używamy rm, nie użyłbym powyższego polecenia w skrypcie! Mądrzejsze użycie to:
(cd /FolderToDeleteWithin && eval $(ls -td *.tar | awk 'NR>7 { print "rm \"" $0 "\""}'))
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:
eval $(ls -td *.tar | awk 'NR>7 { print "VarName="$1 }'); echo "$VarName"
Na podstawie odpowiedzi Izaaka Freemana, ale działa na dowolnym katalogu:
ls -1tr $PATH_TO_DELETE/* | head -n -10 | xargs -d '\n' rm -f --
Możesz także ustawić prefiks, np $PATH_TO_DELETE/testFi*
Jeśli użyjesz *po PATH lsto wypisze bezwzględne nazwy plików, przynajmniej w „moim” bashu :)