Usuń wszystkie oprócz najnowszych plików X w bash

157

Czy istnieje prosty sposób, w całkiem standardowym środowisku UNIX z bash, aby uruchomić polecenie usunięcia wszystkich plików X z katalogu oprócz najnowszych?

Aby dać trochę więcej konkretnego przykładu, wyobraź sobie jakąś pracę crona zapisującą plik (powiedzmy, plik dziennika lub sparowaną kopię zapasową) do katalogu co godzinę. Chciałbym mieć uruchomione inne zadanie crona, które usuwałoby najstarsze pliki w tym katalogu, aż będzie ich mniej niż, powiedzmy, 5.

Dla jasności jest tylko jeden plik, którego nigdy nie należy usuwać.

Matt Sheppard
źródło

Odpowiedzi:

117

Problemy z istniejącymi odpowiedziami:

  • niemożność obsługi nazw plików z osadzonymi spacjami lub znakami nowej linii.
    • w przypadku rozwiązań, które wywołują rmbezpośrednio niecytowane polecenie substitution ( rm `...`), istnieje dodatkowe ryzyko niezamierzonego globowania.
  • niezdolność do rozróżnienia plików i katalogów (tj. jeśli katalogi znalazły się wśród 5 ostatnio zmodyfikowanych elementów systemu plików, efektywnie zachowałbyś mniej niż 5 plików, a zastosowanie rmdo katalogów nie powiedzie się).

Odpowiedź wnoise rozwiązuje te problemy, ale rozwiązanie jest specyficzne dla GNU (i dość złożone).

Oto pragmatyczne, zgodne z POSIX rozwiązanie, które ma tylko jedno zastrzeżenie : nie obsługuje nazw plików z osadzonymi znakami nowej linii - ale nie uważam tego za prawdziwą troskę większości ludzi.

Dla przypomnienia, oto wyjaśnienie, dlaczego generalnie analizowanie lswyników nie jest dobrym pomysłem : http://mywiki.wooledge.org/ParsingLs

ls -tp | grep -v '/$' | tail -n +6 | xargs -I {} rm -- {}

Powyższe jest nieefektywne , ponieważ xargsmusi wywołać rmjeden raz dla każdej nazwy pliku.
Twoja platforma xargsmoże pozwolić ci rozwiązać ten problem:

Jeśli masz GNU xargs , użyj -d '\n', co sprawia, xargsże każdy wiersz wejściowy jest traktowany jako oddzielny argument, a jednocześnie przekazuje tyle argumentów, ile zmieści się w wierszu poleceń jednocześnie :

ls -tp | grep -v '/$' | tail -n +6 | xargs -d '\n' -r rm --

-r( --no-run-if-empty) zapewnia, że rmnie jest wywoływana, jeśli nie ma danych wejściowych.

Jeśli masz BSD xargs (w tym na macOS ), możesz użyć -0do obsługi NULoddzielonych danych wejściowych, po pierwszej translacji znaków nowej linii na NUL( 0x0) znaki., Co również przekazuje (zwykle) wszystkie nazwy plików naraz (będzie również działać z GNU xargs):

ls -tp | grep -v '/$' | tail -n +6 | tr '\n' '\0' | xargs -0 rm --

Wyjaśnienie:

  • ls -tpwypisuje nazwy elementów systemu plików posortowane według czasu, kiedy zostały ostatnio zmodyfikowane, w porządku malejącym (najpierw ostatnio zmodyfikowane elementy) ( -t), z katalogami drukowanymi z końcem /oznaczającym je jako takie ( -p).
  • grep -v '/$'następnie usuwa katalogi z wynikowego listingu, pomijając ( -v) wiersze, które mają na końcu /( /$).
    • Uwaga : ponieważ dowiązanie symboliczne wskazujące na katalog z technicznego punktu widzenia nie jest katalogiem, takie dowiązania symboliczne nie zostaną wykluczone.
  • tail -n +6pomija pierwsze 5 wpisów na liście, w efekcie zwraca wszystkie oprócz 5 ostatnio zmodyfikowanych plików, jeśli takie istnieją.
    Zauważ, że aby wykluczyć Npliki, N+1należy je przekazać do tail -n +.
  • xargs -I {} rm -- {}(i jego odmiany) następnie wywołuje rmna wszystkich tych plikach; jeśli w ogóle nie ma dopasowań, xargsnic nie zrobi.
    • xargs -I {} rm -- {}definiuje symbol zastępczy, {}który reprezentuje każdy wiersz wejściowy jako całość , więc rmjest następnie wywoływany raz dla każdego wiersza wejściowego, ale z nazwami plików z osadzonymi spacjami obsługiwanymi poprawnie.
    • --we wszystkich przypadkach gwarantuje, że wszystkie nazwy plików, które zdarzają się na początek -nie są mylone z opcji użytkownika rm.

Zmienność w oryginalnym problem, w przypadku zgodnych plików muszą być przetwarzane indywidualnie lub zebrane w tablicy powłoki :

# One by one, in a shell loop (POSIX-compliant):
ls -tp | grep -v '/$' | tail -n +6 | while IFS= read -r f; do echo "$f"; done

# One by one, but using a Bash process substitution (<(...), 
# so that the variables inside the `while` loop remain in scope:
while IFS= read -r f; do echo "$f"; done < <(ls -tp | grep -v '/$' | tail -n +6)

# Collecting the matches in a Bash *array*:
IFS=$'\n' read -d '' -ra files  < <(ls -tp | grep -v '/$' | tail -n +6)
printf '%s\n' "${files[@]}" # print array elements
mklement0
źródło
2
Z pewnością lepsze niż większość innych odpowiedzi tutaj, więc cieszę się, że mogę udzielić mojego wsparcia, nawet jeśli uważam, że ignorowanie przypadku nowej linii jest rzeczą, którą należy robić z ostrożnością.
Charles Duffy
2
Jeśli lsnie znajdujesz się w bieżącym katalogu, ścieżki do plików będą zawierały „/”, co oznacza, że grep -v '/'nic nie będzie pasować. Uważam, grep -v '/$'że chcesz tylko wykluczyć katalogi.
waldol 1
1
@ waldol1: Dzięki; Zaktualizowałem odpowiedź, aby uwzględnić twoją sugestię, co również sprawia, że greppolecenie jest koncepcyjnie jaśniejsze. Należy jednak pamiętać, że można opisać problem, że nie mają powierzchniowe z pojedynczej ścieżki katalogu; np. ls -p /private/varnadal będzie drukować tylko nazwy plików. Tylko jeśli przekazałeś wiele argumentów plikowych (zazwyczaj przez glob), zobaczysz rzeczywiste ścieżki w wynikach; np. ls -p /private/var/*(i zobaczysz również zawartość pasujących podkatalogów, chyba że również je uwzględnisz -d).
mklement0
108

Usuń wszystkie ostatnie pliki z katalogu z wyjątkiem 5 (lub dowolnej liczby).

rm `ls -t | awk 'NR>5'`
Espo
źródło
2
Potrzebowałem tego tylko do rozważenia moich plików archiwalnych. zmiana ls -tnals -td *.bz2
James T Snell,
3
Użyłem tego dla katalogów, zmieniając go na rm -rf ls -t | awk 'NR>1'(chciałem tylko najnowszy). Dzięki!
lohiaguitar91
11
ls -t | awk 'NR>5' | xargs rm -f jeśli wolisz rury i musisz pominąć błąd, jeśli nie ma nic do usunięcia.
H2ONaCl
16
Być może zwięzłe i czytelne, ale niebezpieczne w użyciu; jeśli spróbujesz usunąć plik utworzony za pomocą touch 'hello * world', spowoduje to usunięcie absolutnie wszystkiego w bieżącym katalogu .
Charles Duffy
1
Mimo że odpowiedź na to pytanie otrzymano w 2008 roku, działa to jak urok i właśnie to, czego potrzebowałem, aby po prostu usunąć stare kopie zapasowe z określonego katalogu. Niesamowite.
Rens Tillmann
86
(ls -t|head -n 5;ls)|sort|uniq -u|xargs rm

Ta wersja obsługuje nazwy ze spacjami:

(ls -t|head -n 5;ls)|sort|uniq -u|sed -e 's,.*,"&",g'|xargs rm
thelsdj
źródło
20
To polecenie nie będzie poprawnie obsługiwać plików ze spacjami w nazwach.
tylerl
5
(ls -t|head -n 5;ls)to grupa poleceń . Drukuje 5 ostatnich plików dwukrotnie. sortłączy ze sobą identyczne linie. uniq -uusuwa duplikaty, tak że wszystkie pliki oprócz ostatnich 5 pozostają. xargs rmwzywa rmkażdego z nich.
Fabien
15
Spowoduje to usunięcie wszystkich plików, jeśli masz 5 lub mniej! Dodaj --no-run-if-emptydo xargsjak w, (ls -t|head -n 5;ls)|sort|uniq -u|xargs --no-run-if-empty rmzaktualizuj odpowiedź.
Gonfi den Tschal
3
Nawet ten, który „obsługuje nazwy ze spacjami”, jest niebezpieczny. Rozważ nazwę, która zawiera dosłowne cudzysłowy: touch 'foo " bar'zrzuci całą resztę polecenia.
Charles Duffy
2
... to bezpieczniejsze w użyciu xargs -d $'\n'niż wstrzyknąć cytaty do swojej treści, choć NUL ograniczającej strumień wejściowy (co wymaga korzystania coś innego niż lsdo rzeczywiście mają rację) jest rozwiązaniem idealnym.
Charles Duffy
59

Prostszy wariant odpowiedzi thelsdj:

ls -tr | head -n -5 | xargs --no-run-if-empty rm 

ls -tr wyświetla wszystkie pliki, zaczynając od najstarszych (-t od najnowszych, -r do tyłu).

head -n -5 wyświetla wszystkie oprócz 5 ostatnich linii (tj. 5 najnowszych plików).

xargs rm wywołuje rm dla każdego wybranego pliku.

Fabien
źródło
15
Trzeba dodać --no-run-if-empty do xargs, aby nie zawodziło, gdy jest mniej niż 5 plików.
Tom
ls -1tr | głowa -n -5 | xargs rm <---------- musisz dodać -1 do ls lub nie otrzymasz listy wyjściowej, z którą head będzie mógł poprawnie działać
Al Joslin
3
@AlJoslin -1jest wartością domyślną, gdy dane wyjściowe są przesyłane do potoku, więc nie jest to obowiązkowe w tym przypadku. Ma to znacznie większe problemy, związane z domyślnym zachowaniem xargspodczas analizowania nazw ze spacjami, cudzysłowami itp.
Charles Duffy
wydaje się, że --no-run-if-emptynie jest rozpoznawany w mojej powłoce. Używam Cmder w systemie Windows.
StayFoolish,
Może być konieczne użycie tej -0opcji, jeśli nazwy plików mogą zawierać spacje. Jednak jeszcze tego nie testowałem. źródło
Keith,
18
find . -maxdepth 1 -type f -printf '%T@ %p\0' | sort -r -z -n | awk 'BEGIN { RS="\0"; ORS="\0"; FS="" } NR > 5 { sub("^[0-9]*(.[0-9]*)? ", ""); print }' | xargs -0 rm -f

Wymaga wyszukiwania GNU dla -printf i sortowania GNU dla -z, awk GNU dla "\ 0" i GNU xargs dla -0, ale obsługuje pliki z osadzonymi znakami nowej linii lub spacjami.

mądry
źródło
2
Jeśli chcesz usunąć katalogi, po prostu zmień -f na -d i dodaj -r do rm. odnaleźć . -maxdepth 1 -type d -printf '% T @% p \ 0' | sort -r -z -n | awk 'BEGIN {RS = "\ 0"; ORS = "\ 0"; FS = ""} NR> 5 {sub ("^ [0-9] * (. [0-9] *)?", ""); print} '| xargs -0 rm -rf
alex
1
Na pierwszy rzut oka jestem zaskoczony złożonością (a właściwie koniecznością) tej awklogiki. Czy brakuje mi niektórych wymagań w pytaniu PO, które sprawiają, że jest to konieczne?
Charles Duffy
@Charles Duffy: Sub () usuwa sygnaturę czasową, według której jest sortowane. Znacznik czasu utworzony przez „% T @” może zawierać część ułamkową. Dzielenie w przestrzeni za pomocą FS przerywa ścieżki z osadzonymi spacjami. Przypuszczam, że usunięcie przez pierwsze prace kosmiczne, ale prawie tak samo trudne do odczytania. Separatorów RS i ORS nie można ustawić w wierszu poleceń, ponieważ mają wartości NUL.
mądry
1
@wnoise, moim zwykłym podejściem do tego jest potokowanie do while read -r -d ' '; IFS= -r -d ''; do ...pętli powłoki - pierwszy odczyt kończy się na spacji, a drugi przechodzi do NUL.
Charles Duffy
@Charles Duffy: Zawsze jestem nieufny wobec surowej skorupy, być może z powodu obaw związanych z cytatami bizantyjskimi. Teraz myślę, że GNU sed -z -e 's/[^ ]* //; 1,5d'jest najwyraźniejsze. (a może sed -n -z -e 's/[^ ]* //; 6,$p'.
mądry
14

Wszystkie te odpowiedzi kończą się niepowodzeniem, gdy w bieżącym katalogu znajdują się katalogi. Oto coś, co działa:

find . -maxdepth 1 -type f | xargs -x ls -t | awk 'NR>5' | xargs -L1 rm

To:

  1. działa, gdy w bieżącym katalogu są katalogi

  2. próbuje usunąć każdy plik, nawet jeśli poprzedniego nie można było usunąć (z powodu uprawnień itp.)

  3. nie powiedzie się, gdy liczba plików w bieżącym katalogu jest nadmierna i xargsnormalnie cię przekręca ( -x)

  4. nie obsługuje spacji w nazwach plików (być może używasz niewłaściwego systemu operacyjnego?)

Charles Wood
źródło
5
Co się stanie, jeśli findzwróci więcej nazw plików, niż można przekazać w pojedynczym wierszu poleceń ls -t? (Podpowiedź: otrzymujesz wiele uruchomień ls -t, z których każdy jest sortowany tylko indywidualnie, zamiast mieć globalnie poprawną kolejność sortowania; w związku z tym ta odpowiedź jest poważnie zepsuta, gdy działa z wystarczająco dużymi katalogami).
Charles Duffy
12
ls -tQ | tail -n+4 | xargs rm

Lista nazw plików według czasu modyfikacji, cytując każdą nazwę pliku. Wyklucz pierwsze 3 (3 najnowsze). Usuń pozostałe.

EDYTUJ po pomocnym komentarzu mklement0 (dzięki!): Poprawiono argument -n + 3 i zauważ, że nie będzie to działać zgodnie z oczekiwaniami, jeśli nazwy plików zawierają nowe linie i / lub katalog zawiera podkatalogi.

znak
źródło
Wydaje się, że -Qopcja nie istnieje na moim komputerze.
Pierre-Adrien Buisson
4
Hmm, opcja ta znajdowała się w podstawowych narzędziach GNU od ~ 20 lat, ale nie jest wymieniana w wariantach BSD. Czy jesteś na Macu?
Mark
Rzeczywiście jestem. Nie sądziłem, że istnieją różnice dla tego rodzaju naprawdę podstawowych poleceń między aktualnymi systemami. Dziękuję za odpowiedź !
Pierre-Adrien Buisson
3
@Mark: ++ dla -Q. Tak, -Qjest rozszerzeniem GNU (tutaj jest specyfikacja POSIXls ). Małe zastrzeżenie (rzadko jest to problem w praktyce): -Qkoduje osadzone znaki nowej linii w nazwach plików jako dosłowne \n, które rmnie zostaną rozpoznane. Aby wykluczyć pierwsze 3 , xargsargument musi +4. Na koniec zastrzeżenie, które dotyczy również większości innych odpowiedzi: twoje polecenie będzie działać zgodnie z przeznaczeniem tylko wtedy, gdy w bieżącym katalogu nie ma podkatalogów .
mklement0
1
Kiedy nie ma nic do usunięcia, masz wezwać xargs z --no-run-if-emptyopcją:ls -tQ | tail -n+4 | xargs --no-run-if-empty rm
Olivier Lecrivain
8

Ignorowanie nowych linii oznacza ignorowanie bezpieczeństwa i dobrego kodowania. Wnoise miał jedyną dobrą odpowiedź. Oto jego odmiana, która umieszcza nazwy plików w tablicy $ x

while IFS= read -rd ''; do 
    x+=("${REPLY#* }"); 
done < <(find . -maxdepth 1 -printf '%T@ %p\0' | sort -r -z -n )
Ian Kelling
źródło
2
Sugerowałbym wyczyszczenie IFS- w przeciwnym razie ryzykujesz utratę końcowych białych znaków z nazw plików. Można to ograniczyć do polecenia odczytu:while IFS= read -rd ''; do
Charles Duffy
1
dlaczego "${REPLY#* }"?
msciwoj
4

Jeśli nazwy plików nie zawierają spacji, zadziała:

ls -C1 -t| awk 'NR>5'|xargs rm

Jeśli nazwy plików zawierają spacje, na przykład

ls -C1 -t | awk 'NR>5' | sed -e "s/^/rm '/" -e "s/$/'/" | sh

Podstawowa logika:

  • uzyskać listę plików w porządku czasowym, jedna kolumna
  • pobierz wszystkie oprócz pierwszych 5 (w tym przykładzie n = 5)
  • pierwsza wersja: wyślij je do rm
  • druga wersja: gen skrypt, który usunie je poprawnie
Mark Harrison
źródło
Nie zapomnij o while readsztuczce radzenia sobie ze spacjami: ls -C1 -t | awk 'NR>5' | while read d ; do rm -rvf "$d" ; done
pinkeen
1
@pinkeen, niezupełnie bezpieczne, jak tam podano. while IFS= read -r dbyłoby trochę lepiej - -rzapobiega używaniu literałów z odwrotnym ukośnikiem readi IFS=zapobiega automatycznemu przycinaniu końcowych białych znaków.
Charles Duffy
4
BTW, jeśli martwisz się o wrogie nazwy plików, jest to niezwykle niebezpieczne podejście. Rozważ plik utworzony za pomocą touch $'hello \'$(rm -rf ~)\' world'; cudzysłowy w nazwie pliku będą przeciwdziałać cudzysłowom, które dodajesz sed, w wyniku czego kod w wykonywanej nazwie pliku.
Charles Duffy
1
(żeby było jasne, „to” powyżej odnosiło się do | shformularza, który zawiera lukę w zabezpieczeniach typu „shell injection”).
Charles Duffy
2

Dzięki zsh

Zakładając, że nie dbasz o obecne katalogi i nie będziesz mieć więcej niż 999 plików (wybierz większą liczbę, jeśli chcesz, lub utwórz pętlę while).

[ 6 -le `ls *(.)|wc -l` ] && rm *(.om[6,999])

W *(.om[6,999])The .plików oznacza, że ośrodki porządek sortowania w górę, mśrodki według daty modyfikacji (wkleja ado czasu dostępu lub cna zmianę węzła), przy czym [6,999]wybiera zakres pliku, więc nie RM 5 Pierwszy.

lolesque
źródło
Intrygujące, ale przez całe życie nie mogłem uruchomić kwalifikatora globalnego sortowania ( om) (żadne sortowanie, które próbowałem, nie przyniosło żadnego efektu - ani w OSX 10.11.2 (próbowałem z zsh 5.0.8 i 5.1.1) , ani Ubuntu 14.04 (zsh 5.0.2)) - czego mi brakuje ?. Jak dla punktu końcowego zakresu: nie trzeba twardym kodu to po prostu wykorzystać -1odnieść się do ostatniego wpisu, a więc obejmować wszystkie pozostałe pliki: [6,-1].
mklement0
2

Zdaję sobie sprawę, że to stary wątek, ale może ktoś na tym skorzysta. To polecenie znajdzie pliki w bieżącym katalogu:

for F in $(find . -maxdepth 1 -type f -name "*_srv_logs_*.tar.gz" -printf '%T@ %p\n' | sort -r -z -n | tail -n+5 | awk '{ print $2; }'); do rm $F; done

Jest to trochę bardziej niezawodne niż niektóre z poprzednich odpowiedzi, ponieważ pozwala ograniczyć domenę wyszukiwania do wyrażeń pasujących do plików. Najpierw znajdź pliki spełniające dowolne warunki. Wydrukuj te pliki ze znacznikami czasu obok nich.

find . -maxdepth 1 -type f -name "*_srv_logs_*.tar.gz" -printf '%T@ %p\n'

Następnie posortuj je według sygnatur czasowych:

sort -r -z -n

Następnie usuń 4 najnowsze pliki z listy:

tail -n+5

Weź drugą kolumnę (nazwę pliku, a nie sygnaturę czasową):

awk '{ print $2; }'

A następnie zawiń to wszystko w oświadczenie:

for F in $(); do rm $F; done

Może to być bardziej szczegółowe polecenie, ale miałem dużo większe szczęście, że mogłem kierować na pliki warunkowe i wykonywać bardziej złożone polecenia przeciwko nim.

TopherGopher
źródło
1

znalazłem interesujące cmd w Sed-Onliners - Usuń ostatnie 3 wiersze - znajdź to idealne miejsce na inny sposób skórowania kota (okej), ale pomysł:

 #!/bin/bash
 # sed cmd chng #2 to value file wish to retain

 cd /opt/depot 

 ls -1 MyMintFiles*.zip > BigList
 sed -n -e :a -e '1,2!{P;N;D;};N;ba' BigList > DeList

 for i in `cat DeList` 
 do 
 echo "Deleted $i" 
 rm -f $i  
 #echo "File(s) gonzo " 
 #read junk 
 done 
 exit 0
tim
źródło
1

Usuwa wszystkie oprócz 10 najnowszych (najbardziej aktualnych) plików

ls -t1 | head -n $(echo $(ls -1 | wc -l) - 10 | bc) | xargs rm

Jeśli mniej niż 10 plików, żaden plik nie zostanie usunięty i otrzymasz: nagłówek błędu: niedozwolona liczba wierszy - 0

Aby policzyć pliki za pomocą bash

fabrice
źródło
1

Potrzebowałem eleganckiego rozwiązania dla busyboxa (routera), wszystkie xargi czy rozwiązania macierzowe były dla mnie bezużyteczne - nie ma tam takiego polecenia. find i mtime nie są właściwą odpowiedzią, ponieważ mówimy o 10 elementach, a niekoniecznie o 10 dniach. Odpowiedź Espo była najkrótsza, najczystsza i prawdopodobnie najbardziej nieodwracalna.

Błędy ze spacjami i brak plików do usunięcia są po prostu rozwiązywane w standardowy sposób:

rm "$(ls -td *.tar | awk 'NR>7')" 2>&-

Wersja nieco bardziej edukacyjna: możemy to wszystko zrobić, jeśli używamy awk w inny sposób. Zwykle używam tej metody do przekazywania (zwracania) zmiennych z awk do sh. Ponieważ cały czas czytamy, że nie można tego zrobić, błagam o różnicę: oto metoda.

Przykład dla plików .tar bez problemu dotyczącego 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 wierszy

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 zaszkodzi 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 w ten sposób przekazać zmienną do sh, po prostu zmodyfikowalibyśmy print (prosta forma, bez spacji):

print "VarName="$1

aby ustawić zmienną VarNamena wartość $1. Za jednym razem można utworzyć wiele zmiennych. To VarNamestaje się normalną zmienną sh i może być później normalnie użyte w skrypcie lub powłoce. Tak więc, aby utworzyć zmienne za pomocą awk i przekazać je z powrotem powłoce:

eval $(ls -td *.tar | awk 'NR>7 { print "VarName=\""$1"\""  }'); echo "$VarName"
Pila
źródło
0
leaveCount=5
fileCount=$(ls -1 *.log | wc -l)
tailCount=$((fileCount - leaveCount))

# avoid negative tail argument
[[ $tailCount < 0 ]] && tailCount=0

ls -t *.log | tail -$tailCount | xargs rm -f
Pavel Tankov
źródło
2
xargsbez -0lub co najmniej -d $'\n'jest niewiarygodne; obserwuj, jak zachowuje się to w przypadku pliku ze spacjami lub cudzysłowami w nazwie.
Charles Duffy
0

Zrobiłem to w skrypcie powłoki bash. Użycie: keep NUM DIRgdzie NUM to liczba plików do zachowania, a DIR to katalog do wyszorowania.

#!/bin/bash
# Keep last N files by date.
# Usage: keep NUMBER DIRECTORY
echo ""
if [ $# -lt 2 ]; then
    echo "Usage: $0 NUMFILES DIR"
    echo "Keep last N newest files."
    exit 1
fi
if [ ! -e $2 ]; then
    echo "ERROR: directory '$1' does not exist"
    exit 1
fi
if [ ! -d $2 ]; then
    echo "ERROR: '$1' is not a directory"
    exit 1
fi
pushd $2 > /dev/null
ls -tp | grep -v '/' | tail -n +"$1" | xargs -I {} rm -- {}
popd > /dev/null
echo "Done. Kept $1 most recent files in $2."
ls $2|wc -l
Sitowie
źródło