Analiza wyniku niels
jest wiarygodna .
Zamiast tego użyj find
do zlokalizowania plików i sort
uporządkowania ich według datownika. Na przykład:
while IFS= read -r -d $'\0' line ; do
file="${line#* }"
# do something with $file here
done < <(find . -maxdepth 1 -printf '%T@ %p\0' \
2>/dev/null | sort -z -n)
Co to wszystko robi?
Najpierw find
polecenia lokalizuje wszystkie pliki i katalogi w bieżącym katalogu ( .
), ale nie w podkatalogach bieżącego katalogu ( -maxdepth 1
), a następnie drukuje:
- Znacznik czasu
- Przestrzeń
- Względna ścieżka do pliku
- Znak NULL
Znacznik czasu jest ważny. Specyfikator %T@
formatu dla -printf
podziału na T
, który wskazuje „czas ostatniej modyfikacji” pliku (mtime) i @
, który wskazuje „sekundy od 1970 roku”, w tym ułamkowe sekundy.
Przestrzeń jest jedynie arbitralnym ogranicznikiem. Pełna ścieżka do pliku jest taka, że możemy się do niego później odwoływać, a znak NULL jest terminatorem, ponieważ jest to niedozwolony znak w nazwie pliku, a zatem daje nam pewność, że dotarliśmy do końca ścieżki do plik.
Podałem 2>/dev/null
tak, aby pliki, do których użytkownik nie ma uprawnień dostępu, zostały wykluczone, ale komunikaty o błędach ich wykluczenia są pomijane.
Wynikiem find
polecenia jest lista wszystkich katalogów w bieżącym katalogu. Lista jest przesyłana potokowo, do sort
której należy:
-z
Traktuj NULL jako znak końca linii zamiast znaku nowej linii.
-n
Sortuj numerycznie
Ponieważ sekundy od 1970 roku zawsze się zwiększają, chcemy pliku, którego znacznik czasu był najmniejszy. Pierwszym wynikiem sort
będzie linia zawierająca najmniejszy numer znacznika czasu. Pozostaje tylko wyodrębnić nazwę pliku.
Wyniki find
, sort
rurociąg przechodzi przez substytucji procesowej na while
którym jest czytać tak, jakby był plik na stdin. while
z kolei wywołuje read
przetwarzanie danych wejściowych.
W kontekście read
ustawiamy IFS
zmienną na nic, co oznacza, że białe znaki nie będą niewłaściwie interpretowane jako separator. read
dowiaduje się -r
, co blokuje ekspansję ucieczki, i -d $'\0'
, co sprawia, że wycofanego z linii ogranicznika NULL, pasujące wyjście z naszej find
, sort
rurociągu.
Pierwszy fragment danych, który reprezentuje najstarszą ścieżkę pliku poprzedzoną znacznikiem czasu i spacją, jest wczytywany do zmiennej line
. Następnie do wyrażenia stosuje się podstawienie parametrów#*
, które po prostu zastępuje wszystkie znaki od początku łańcucha do pierwszej spacji, w tym spacji, niczym. To usuwa znacznik czasu modyfikacji, pozostawiając tylko pełną ścieżkę do pliku.
W tym momencie nazwa pliku jest przechowywana $file
i możesz z nim zrobić, co chcesz. Kiedy skończysz robić coś z $file
tym while
oświadczenie będzie pętli, a read
polecenie zostanie ponownie wykonany, wydobycia następny i następny kawałek nazwy pliku.
Czy nie ma prostszego sposobu?
Nie. Prostsze sposoby są wadliwe.
Jeśli użyjesz ls -t
i potokujesz do head
lub tail
(lub cokolwiek ), będziesz łamał pliki z nowymi liniami w nazwach plików. Jeśli mv $(anything)
następnie pliki z białą spacją w nazwie spowodują uszkodzenie. Jeśli mv "$(anything)"
następnie pliki z końcowymi znakami nowej nazwy spowodują uszkodzenie. Jeśli tak read
nie -d $'\0'
jest, włamujesz się do plików ze spacjami w ich nazwach.
Być może w szczególnych przypadkach wiesz na pewno, że prostszy sposób jest wystarczający, ale nigdy nie powinieneś zapisywać takich założeń w skryptach, jeśli możesz tego uniknąć.
Rozwiązanie
#!/usr/bin/env bash
# move to the first argument
dest="$1"
# move from the second argument or .
source="${2-.}"
# move the file count in the third argument or 20
limit="${3-20}"
while IFS= read -r -d $'\0' line ; do
file="${line#* }"
echo mv "$file" "$dest"
let limit-=1
[[ $limit -le 0 ]] && break
done < <(find "$source" -maxdepth 1 -printf '%T@ %p\0' \
2>/dev/null | sort -z -n)
Zadzwoń jak:
move-oldest /mnt/backup/ /var/log/foo/ 20
Aby przenieść najstarsze 20 plików z /var/log/foo/
do /mnt/backup/
.
Pamiętaj, że dołączam pliki i katalogi. W przypadku plików dodaj tylko -type f
do find
wywołania.
Dzięki
Dzięki enzotib i Павел Танков za ulepszenia tej odpowiedzi.