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