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ą
rm
bezpoś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
rm
do 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 ls
wynikó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ż xargs
musi wywołać rm
jeden raz dla każdej nazwy pliku.
Twoja platforma xargs
moż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 rm
nie jest wywoływana, jeśli nie ma danych wejściowych.
Jeśli masz BSD xargs
(w tym na macOS ), możesz użyć -0
do obsługi NUL
oddzielonych 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 -tp
wypisuje 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 +6
pomija 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ć N
pliki, N+1
należy je przekazać do tail -n +
.
xargs -I {} rm -- {}
(i jego odmiany) następnie wywołuje rm
na wszystkich tych plikach; jeśli w ogóle nie ma dopasowań, xargs
nic nie zrobi.
xargs -I {} rm -- {}
definiuje symbol zastępczy, {}
który reprezentuje każdy wiersz wejściowy jako całość , więc rm
jest 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
ls
nie znajdujesz się w bieżącym katalogu, ścieżki do plików będą zawierały „/”, co oznacza, żegrep -v '/'
nic nie będzie pasować. Uważam,grep -v '/$'
że chcesz tylko wykluczyć katalogi.grep
polecenie 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/var
nadal 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
).Usuń wszystkie ostatnie pliki z katalogu z wyjątkiem 5 (lub dowolnej liczby).
źródło
ls -t
nals -td *.bz2
ls -t | awk 'NR>1'
(chciałem tylko najnowszy). Dzięki!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.touch 'hello * world'
, spowoduje to usunięcie absolutnie wszystkiego w bieżącym katalogu .Ta wersja obsługuje nazwy ze spacjami:
źródło
(ls -t|head -n 5;ls)
to grupa poleceń . Drukuje 5 ostatnich plików dwukrotnie.sort
łączy ze sobą identyczne linie.uniq -u
usuwa duplikaty, tak że wszystkie pliki oprócz ostatnich 5 pozostają.xargs rm
wzywarm
każdego z nich.--no-run-if-empty
doxargs
jak w,(ls -t|head -n 5;ls)|sort|uniq -u|xargs --no-run-if-empty rm
zaktualizuj odpowiedź.touch 'foo " bar'
zrzuci całą resztę polecenia.xargs -d $'\n'
niż wstrzyknąć cytaty do swojej treści, choć NUL ograniczającej strumień wejściowy (co wymaga korzystania coś innego niżls
do rzeczywiście mają rację) jest rozwiązaniem idealnym.Prostszy wariant odpowiedzi thelsdj:
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.
źródło
-1
jest 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 zachowaniemxargs
podczas analizowania nazw ze spacjami, cudzysłowami itp.--no-run-if-empty
nie jest rozpoznawany w mojej powłoce. Używam Cmder w systemie Windows.-0
opcji, jeśli nazwy plików mogą zawierać spacje. Jednak jeszcze tego nie testowałem. źródłoWymaga 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.
źródło
awk
logiki. Czy brakuje mi niektórych wymagań w pytaniu PO, które sprawiają, że jest to konieczne?while read -r -d ' '; IFS= -r -d ''; do ...
pętli powłoki - pierwszy odczyt kończy się na spacji, a drugi przechodzi do NUL.sed -z -e 's/[^ ]* //; 1,5d'
jest najwyraźniejsze. (a możesed -n -z -e 's/[^ ]* //; 6,$p'
.Wszystkie te odpowiedzi kończą się niepowodzeniem, gdy w bieżącym katalogu znajdują się katalogi. Oto coś, co działa:
To:
działa, gdy w bieżącym katalogu są katalogi
próbuje usunąć każdy plik, nawet jeśli poprzedniego nie można było usunąć (z powodu uprawnień itp.)
nie powiedzie się, gdy liczba plików w bieżącym katalogu jest nadmierna i
xargs
normalnie cię przekręca (-x
)nie obsługuje spacji w nazwach plików (być może używasz niewłaściwego systemu operacyjnego?)
źródło
find
zwró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).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.
źródło
-Q
opcja nie istnieje na moim komputerze.-Q
. Tak,-Q
jest rozszerzeniem GNU (tutaj jest specyfikacja POSIXls
). Małe zastrzeżenie (rzadko jest to problem w praktyce):-Q
koduje osadzone znaki nowej linii w nazwach plików jako dosłowne\n
, którerm
nie zostaną rozpoznane. Aby wykluczyć pierwsze 3 ,xargs
argument 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 .--no-run-if-empty
opcją:ls -tQ | tail -n+4 | xargs --no-run-if-empty rm
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
źródło
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
"${REPLY#* }"
?Jeśli nazwy plików nie zawierają spacji, zadziała:
Jeśli nazwy plików zawierają spacje, na przykład
Podstawowa logika:
źródło
while read
sztuczce radzenia sobie ze spacjami:ls -C1 -t | awk 'NR>5' | while read d ; do rm -rvf "$d" ; done
while IFS= read -r d
byłoby trochę lepiej --r
zapobiega używaniu literałów z odwrotnym ukośnikiemread
iIFS=
zapobiega automatycznemu przycinaniu końcowych białych znaków.touch $'hello \'$(rm -rf ~)\' world'
; cudzysłowy w nazwie pliku będą przeciwdziałać cudzysłowom, które dodajeszsed
, w wyniku czego kod w wykonywanej nazwie pliku.| sh
formularza, który zawiera lukę w zabezpieczeniach typu „shell injection”).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).
W
*(.om[6,999])
The.
plików oznacza, żeo
środki porządek sortowania w górę,m
środki według daty modyfikacji (wklejaa
do czasu dostępu lubc
na zmianę węzła), przy czym[6,999]
wybiera zakres pliku, więc nie RM 5 Pierwszy.źródło
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ć-1
odnieść się do ostatniego wpisu, a więc obejmować wszystkie pozostałe pliki:[6,-1]
.Zdaję sobie sprawę, że to stary wątek, ale może ktoś na tym skorzysta. To polecenie znajdzie pliki w bieżącym katalogu:
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.
Następnie posortuj je według sygnatur czasowych:
Następnie usuń 4 najnowsze pliki z listy:
Weź drugą kolumnę (nazwę pliku, a nie sygnaturę czasową):
A następnie zawiń to wszystko w oświadczenie:
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.
źródło
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ł:
źródło
Usuwa wszystkie oprócz 10 najnowszych (najbardziej aktualnych) plików
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
źródło
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:
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”.
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 wierszyprint "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 zaszkodzi 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 w ten sposób przekazać zmienną do sh, po prostu zmodyfikowalibyśmy print (prosta forma, bez spacji):
aby ustawić zmienną
VarName
na wartość$1
. Za jednym razem można utworzyć wiele zmiennych. ToVarName
staje 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:źródło
źródło
xargs
bez-0
lub co najmniej-d $'\n'
jest niewiarygodne; obserwuj, jak zachowuje się to w przypadku pliku ze spacjami lub cudzysłowami w nazwie.Zrobiłem to w skrypcie powłoki bash. Użycie:
keep NUM DIR
gdzie NUM to liczba plików do zachowania, a DIR to katalog do wyszorowania.źródło