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