Jak znaleźć najstarszy plik w drzewie katalogów

72

Szukam powłoki jednowierszowej, aby znaleźć najstarszy plik w drzewie katalogów.

Marius Gedminas
źródło

Odpowiedzi:

72

Działa to (zaktualizowano w celu uwzględnienia sugestii Daniela Anderssona):

find -type f -printf '%T+ %p\n' | sort | head -n 1
Marius Gedminas
źródło
8
Mniej pisania:find -type f -printf '%T+ %p\n' | sort | head -1
Daniel Andersson
1
Dostaję puste miejsce, ponieważ moja pierwsza linia z tego findjest pusta z powodu faktu, że nazwa pliku zawiera nowy wiersz.
皞 皞
1
Czy mogę zapytać, czy wykorzystuje to datę utworzenia czy modyfikację?
MrMesees
1
Linux nigdzie nie przechowuje daty utworzenia pliku [*]. Wykorzystuje datę modyfikacji. [*] to nie jest prawdą; ext4 przechowuje datę utworzenia i-węzła, ale nie jest ujawniana przez żadne wywołania systemowe i aby ją zobaczyć, musisz użyć debugfs.)
Marius Gedminas
11

Ten jest trochę bardziej przenośny i ponieważ nie opiera się na findrozszerzeniu GNU -printf, więc działa również na BSD / OS X:

find . -type f -print0 | xargs -0 ls -ltr | head -n 1

Jedynym minusem jest to, że jest on nieco ograniczony do wielkości ARG_MAX(co powinno być nieistotne w przypadku większości nowszych jąder). Więc jeśli getconf ARG_MAXzwrócono więcej niż znaki (262 144 w moim systemie), nie daje to poprawnego wyniku. Nie jest również zgodny z POSIX, ponieważ -print0i xargs -0nie jest.

Poniżej przedstawiono kilka rozwiązań tego problemu: Jak znaleźć najnowszy (najnowszy, najwcześniejszy, najstarszy) plik w katalogu? - Wiki Grega

slhck
źródło
To też działa, ale powoduje również xargs: ls: terminated by signal 13błąd jako efekt uboczny. Zgaduję, że to SIGPIPE. Nie mam pojęcia, dlaczego nie pojawia się podobny błąd, gdy przesyłam dane wyjściowe sortowania do głowicy w swoim rozwiązaniu.
Marius Gedminas
Twoja wersja jest również łatwiejsza do pisania z pamięci. :-)
Marius Gedminas
Tak, to zepsuta rura. Nie dostaję tego w obu wersjach GNU i BSD wszystkich tych poleceń, ale headmyślę , że to polecenie, które kończy pracę, gdy przeczyta linię, a tym samym „łamie” potok, tak myślę. Błąd sortnie pojawia się, ponieważ wydaje się, że nie narzeka, ale lsw innym przypadku.
slhck
4
To się psuje, jeśli istnieje tak wiele nazw plików, które xargstrzeba wywołać lswięcej niż jeden raz. W takim przypadku posortowane dane wyjściowe tych wielokrotnych wywołań kończą się konkatenacją, kiedy należy je połączyć.
Nicole Hamilton,
2
Myślę, że jest to gorsze niż opublikowanie skryptu, który zakłada, że ​​nazwy plików nigdy nie zawierają spacji. Często będą one działać, ponieważ nazwy plików nie mają spacji. A kiedy zawodzą, pojawia się błąd. Ale to raczej nie zadziała w prawdziwych przypadkach, a niepowodzenie pozostanie nieodkryte. Nie na każdym drzewie katalogów na tyle duży, że można po prostu lsgo i gałki ocznej najstarszy plik rozwiązanie prawdopodobnie zostanie przekroczony limit długości wiersza poleceń, powodując lspowoływać się wielokrotnie. Otrzymasz złą odpowiedź, ale nigdy się nie dowiesz.
Nicole Hamilton
11

Następujące polecenia polecenia gwarantują działanie z dowolnymi dziwnymi nazwami plików:

find -type f -printf "%T+ %p\0" | sort -z | grep -zom 1 ".*" | cat

find -type f -printf "%T@ %T+ %p\0" | \
    sort -nz | grep -zom 1 ".*" | sed 's/[^ ]* //'

stat -c "%y %n" "$(find -type f -printf "%T@ %p\0" | \
    sort -nz | grep -zom 1 ".*" | sed 's/[^ ]* //')"

Użycie pustego bajtu ( \0) zamiast znaku wysuwu wiersza ( \n) zapewnia, że ​​wyjście find będzie nadal zrozumiałe, jeśli jedna z nazw plików zawiera znak wysuwu wiersza.

-zPrzełącznik umożliwia zarówno rodzaj i grep interpretować jedynie jako zerowe bajty wycofanych z linii znaków. Ponieważ nie ma takiego przełącznika na głowę, używamy grep -m 1zamiast tego (tylko jedno wystąpienie).

Polecenia są uporządkowane według czasu wykonania (mierzonego na moim komputerze).

  • Pierwsze polecenie będzie najwolniejsze, ponieważ najpierw musi przekonwertować mtime każdego pliku na format czytelny dla człowieka, a następnie posortować te ciągi. Rurociągowanie do kota zapobiega zabarwieniu produktu wyjściowego.

  • Drugie polecenie jest nieco szybsze. Chociaż nadal wykonuje konwersję daty, sortowanie numeryczne ( sort -n) sekund, które upłynęły od epoki Uniksa, jest nieco szybsze. sed usuwa sekundy od epoki Uniksa.

  • Ostatnie polecenie w ogóle nie powoduje konwersji i powinno być znacznie szybsze niż pierwsze dwa. Sama komenda find nie wyświetla czasu mtime najstarszego pliku, więc statystyki są potrzebne.

Powiązane strony man: find - grep - sed - sort - stat

Dennis
źródło
5

Chociaż zaakceptowana odpowiedź i inni tutaj wykonują zadanie, jeśli masz bardzo duże drzewo, wszystkie posortują całą wiązkę plików.

Lepiej byłoby, gdybyśmy mogli je po prostu wymienić i śledzić najstarsze, bez konieczności sortowania.

Właśnie dlatego wymyśliłem to alternatywne rozwiązanie:

ls -lRU $PWD/* | awk 'BEGIN {cont=0; oldd=strftime("%Y%m%d"); } { gsub(/-/,"",$6); if (substr($1,0,1)=="/") { pat=substr($1,0,length($0)-1)"/"; }; if( $6 != "") {if ( $6 < oldd ) { oldd=$6; oldf=pat$8; }; print $6, pat$8; count++;}} END { print "Oldest date: ", oldd, "\nFile:", oldf, "\nTotal compared: ", count}'

Mam nadzieję, że może być pomocna, nawet jeśli pytanie jest nieco stare.


Edycja 1: zmiany te umożliwiają parsowanie plików i katalogów ze spacjami. Jest wystarczająco szybki, aby wydać go w katalogu głównym /i znaleźć najstarszy plik w historii.

ls -lRU --time-style=long-iso "$PWD"/* | awk 'BEGIN {cont=0; oldd=strftime("%Y%m%d"); } { gsub(/-/,"",$6); if (substr($0,0,1)=="/") { pat=substr($0,0,length($0)-1)"/"; $6="" }; if( $6 ~ /^[0-9]+$/) {if ( $6 < oldd ) { oldd=$6; oldf=$8; for(i=9; i<=NF; i++) oldf=oldf $i; oldf=pat oldf; }; count++;}} END { print "Oldest date: ", oldd, "\nFile:", oldf, "\nTotal compared: ", count}'

Wyjaśnienie polecenia:

  • ls -lRU --time-style = long-iso "$ PWD" / * wyświetla wszystkie pliki (*), długi format (l), rekurencyjnie (R), bez sortowania (U), aby było szybkie, i potokuje go do awk
  • Awk, a następnie ROZPOCZNIJ, zerując licznik (opcjonalnie w przypadku tego pytania) i ustawiając najstarszą datę, która ma być dzisiaj, sformatuj YearMonthDay.
  • Najpierw główna pętla
    • Pobiera szóste pole, datę, format Rok-Miesiąc-Dzień i zmienia je na YearMonthDay (jeśli twój ls nie wyświetla danych wyjściowych w ten sposób, być może będziesz musiał go dostroić).
    • Używając rekurencyjnych, pojawią się wiersze nagłówka dla wszystkich katalogów, w postaci / directory / tutaj :. Złap tę linię w zmienną pat. (podstawiając ostatnie „:” na „/”). I ustawia 6 $ na nic, aby uniknąć użycia wiersza nagłówka jako prawidłowej linii pliku.
    • jeśli pole 6 $ ma prawidłowy numer, jest to data. Porównaj to ze starą datą oldd.
    • Czy to jest starsze? Następnie zapisz nowe wartości dla starej daty oldd i starej nazwy pliku oldf. BTW, oldf to nie tylko 8 pole, ale od 8 do końca. Dlatego pętla do konkatenacji od 8. do NF (koniec).
    • Policz zaliczki o jeden
    • ZAKOŃCZ, drukując wynik

Uruchamianie:

~ $ czas ls -lRU "$ PWD" / * | awk itp

Najstarsza data: 19691231

Plik: /home/.../.../backupold/.../EXAMPLES/how-to-program.txt

Porównywane ogółem: 111438

prawdziwe 0m1.135s

użytkownik 0m0,872s

sys 0m0,760s


EDIT 2: Ta sama koncepcja, lepszym rozwiązaniem przy użyciu findpatrzeć na czas dostępu (używać %Tod pierwszego printfdo czasu modyfikacji lub %Co zmianie stanu zamiast).

find . -wholename "*" -type f -printf "%AY%Am%Ad %h/%f\n" | awk 'BEGIN {cont=0; oldd=strftime("%Y%m%d"); } { if ($1 < oldd) { oldd=$1; oldf=$2; for(i=3; i<=NF; i++) oldf=oldf " " $i; }; count++; } END { print "Oldest date: ", oldd, "\nFile:", oldf, "\nTotal compared: ", count}'

EDYCJA 3: Poniższa komenda wykorzystuje czas modyfikacji, a także drukuje przyrostowy postęp, ponieważ wyszukuje coraz starsze pliki, co jest przydatne, gdy masz niepoprawne znaczniki czasu (np. 1970-01-01):

find . -wholename "*" -type f -printf "%TY%Tm%Td %h/%f\n" | awk 'BEGIN {cont=0; oldd=strftime("%Y%m%d"); } { if ($1 < oldd) { oldd=$1; oldf=$2; for(i=3; i<=NF; i++) oldf=oldf " " $i; print oldd " " oldf; }; count++; } END { print "Oldest date: ", oldd, "\nFile:", oldf, "\nTotal compared: ", count}'
Dr Beco
źródło
Nadal wymaga dostosowania, aby zaakceptować pliki ze spacjami. Zrobię to wkrótce.
Dr Beco
Myślę, że parsowanie ls dla plików ze spacjami nie jest dobrym pomysłem. Może za pomocą find.
Dr Beco,
Po prostu uruchom go w całym drzewie „/”. Czas spędzony: Razem porównany: 585744 rzeczywisty 2m14.017s użytkownik 0m8.181s sys 0m8.473s
Dr Beco
Używanie lsjest złe w skryptach, ponieważ jego dane wyjściowe nie są przeznaczone dla komputerów, formatowanie danych wyjściowych różni się w zależności od implementacji. Jak już wspomniano, findjest dobry do pisania skryptów, ale może być również dobre dodanie tej informacji przed poinformowaniem o lsrozwiązaniach.
Sampo Sarrala
4

Użyj ls - strona podręcznika informuje, jak zamówić katalog.

ls -clt | head -n 2

-N 2 jest tak, że nie otrzymujesz „sumy” na wyjściu. Jeśli chcesz tylko nazwę pliku.

ls -t | head -n 1

A jeśli potrzebujesz listy w normalnej kolejności (uzyskanie najnowszego pliku)

ls -tr | head -n 1

Znacznie łatwiejsze niż użycie find, znacznie szybsze i bardziej niezawodne - nie musisz się martwić formatami nazewnictwa plików. Powinien również działać na prawie wszystkich systemach.

użytkownik1363990
źródło
6
Działa to tylko wtedy, gdy pliki znajdują się w jednym katalogu, podczas gdy moje pytanie dotyczyło drzewa katalogów.
Marius Gedminas
2
find ! -type d -printf "%T@ %p\n" | sort -n | head -n1
Okki
źródło
To nie będzie działać poprawnie, jeśli istnieją pliki starsze niż 9 września 2001 (1000000000 sekund od epoki Uniksa). Aby włączyć sortowanie numeryczne, użyj sort -n.
Dennis
Pomaga mi to znaleźć plik, ale trudno jest zobaczyć, ile ma lat bez uruchomienia drugiego polecenia :)
Marius Gedminas
0

Wydaje się, że przez „najstarsze” większość ludzi przyjęło, że chodziło o „najstarszy czas modyfikacji”. Prawdopodobnie jest to poprawione, zgodnie z najbardziej rygorystyczną interpretacją „najstarszego”, ale w przypadku, gdybyś chciał tego z najstarszym czasem dostępu , zmodyfikowałbym najlepszą odpowiedź w ten sposób:

find -type f -printf '%A+ %p\n' | sort | head -n 1

Zwróć uwagę na %A+.

PenguinLust
źródło
-1
set $(find /search/dirname -type f -printf '%T+ %h/%f\n' | sort | head -n 1) && echo $2
  • find ./search/dirname -type f -printf '%T+ %h/%f\n' drukuje daty i nazwy plików w dwóch kolumnach.
  • sort | head -n1 zachowuje linię odpowiadającą najstarszemu plikowi.
  • echo $2 wyświetla drugą kolumnę, tj. nazwę pliku.
Dima
źródło
1
Witamy w Super User! Chociaż może to odpowiedzieć na pytanie, lepszym rozwiązaniem byłoby podanie wyjaśnienia, dlaczego tak się dzieje.
DavidPostill
1
Uwaga: kilka osób poprosiło także o wyjaśnienie poprzedniej (identycznej) usuniętej odpowiedzi.
DavidPostill
Na co trudno odpowiedzieć? znajdź ./search/dirname -type f -printf '% T +% h /% f \ n' | sortuj | head -n 1 Pokazuje dwie kolumny jako czas i ścieżkę do pliku. Konieczne jest usunięcie pierwszej kolumny. Za pomocą set i echa 2 $
Dima
1
Powinieneś podać wyjaśnienia zamiast wklejania wiersza poleceń, zgodnie z żądaniem kilku innych użytkowników.
Ob1lan
1
Czym różni się to od przyjętej odpowiedzi?
Ramhound,