Jak znaleźć najstarszy plik w drzewie katalogów


Odpowiedzi:


72

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

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

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


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


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}'

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.


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

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+.


-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.

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,
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.