Analiza wyniku niels jest wiarygodna .
Zamiast tego użyj finddo zlokalizowania plików i sortuporzą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 findpolecenia 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 -printfpodział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/nulltak, 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 findpolecenia jest lista wszystkich katalogów w bieżącym katalogu. Lista jest przesyłana potokowo, do sortktó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 sortbędzie linia zawierająca najmniejszy numer znacznika czasu. Pozostaje tylko wyodrębnić nazwę pliku.
Wyniki find, sortrurociąg przechodzi przez substytucji procesowej na whilektórym jest czytać tak, jakby był plik na stdin. whilez kolei wywołuje readprzetwarzanie danych wejściowych.
W kontekście readustawiamy IFSzmienną na nic, co oznacza, że białe znaki nie będą niewłaściwie interpretowane jako separator. readdowiaduje się -r, co blokuje ekspansję ucieczki, i -d $'\0', co sprawia, że wycofanego z linii ogranicznika NULL, pasujące wyjście z naszej find, sortrurocią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 $filei możesz z nim zrobić, co chcesz. Kiedy skończysz robić coś z $filetym whileoświadczenie będzie pętli, a readpolecenie 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 -ti potokujesz do headlub 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 readnie -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 fdo findwywołania.
Dzięki
Dzięki enzotib i Павел Танков za ulepszenia tej odpowiedzi.