2011-05-04 15 views
75

W Bash, chciałbym utworzyć funkcję, która zwraca nazwę pliku najnowszego pliku, który pasuje do określonego wzorca. Na przykład, mam katalog plików takich jak:Funkcja Bash, aby znaleźć najnowszy plik pasujący do wzorca

Directory/ 
    a1.1_5_1 
    a1.2_1_4 
    b2.1_0 
    b2.2_3_4 
    b2.3_2_0 

Chcę najnowszy plik, który zaczyna się od "b2". Jak to zrobić w bash? Muszę to mieć w moim skrypcie ~/.bash_profile.

+1

patrz http://superuser.com/questions/294161/unix-linux-find-and-sort-by-date-modyfikowany, aby uzyskać więcej wskazówek dotyczących odpowiedzi. Sortowanie jest kluczowym krokiem, aby uzyskać najnowszy plik –

Odpowiedz

139

Polecenie ls ma parametr sortowanie według czasu. Możesz wtedy pobrać pierwszy (najnowszy) z head -1.

ls -t b2* | head -1 

Ale uwaga: Why you shouldn't parse the output of ls

moja osobista opinia: parsowania ls jest tylko niebezpieczne, gdy nazwy plików mogą zawierać zabawne postacie jak spacjami lub znakami nowej linii. Jeśli możesz zagwarantować, że nazwy plików nie będą zawierać zabawnych znaków, parsowanie ls jest całkiem bezpieczne.

Jeśli tworzysz skrypt, który ma być uruchamiany przez wiele osób w wielu systemach w wielu różnych sytuacjach, bardzo zalecam, aby nie parsować ls.

Oto jak to zrobić „prawo”. How can I find the latest (newest, earliest, oldest) file in a directory?

unset -v latest 
for file in "$dir"/*; do 
    [[ $file -nt $latest ]] && latest=$file 
done 
+5

Uwaga dla innych: jeśli robisz to dla katalogu, możesz dodać opcję -d do ls, jak ten 'ls -td | head -1 ' –

+4

Link [parsing LS] (http://mywiki.wooledge.org/ParsingLs) mówi, aby tego nie robić i zaleca metody w [BashFAQ 99] (http://mywiki.wooledge.org/BashFAQ/099). Szukam raczej jednego liniowca niż czegoś kuloodpornego, aby uwzględnić go w skrypcie, więc będę dalej analizował nieprzyzwoicie jak @lesmana. – Eponymous

+0

@Eponymous: Jeśli szukasz jednej linijki bez użycia delikatnych 'ls',' printf "% s \ n" b2 * | head -1' zrobi to za ciebie. –

2

Nietypowe nazwy plików (takie jak pliku zawierającego poprawny \n charakter mogą siać spustoszenie z tego rodzaju parsowania Oto sposób to zrobić w Perl:

perl -le '@sorted = map {$_->[0]} 
        sort {$a->[1] <=> $b->[1]} 
        map {[$_, -M $_]} 
        @ARGV; 
      print $sorted[0] 
' b2* 

to tam Schwartzian transform wykorzystywane

+0

Niech Schwartz będzie z tobą! –

+0

ta odpowiedź może zadziałać, ale nie zaufałbym jej, biorąc pod uwagę słabą dokumentację. –

+1

słaba dokumentacja? pochodzi z wikipage ... –

4

jest to możliwe z realizacja. wymagana funkcja Bash:

# Print the newest file, if any, matching the given pattern 
# Example usage: 
# newest_matching_file 'b2*' 
# WARNING: Files whose names begin with a dot will not be checked 
function newest_matching_file 
{ 
    # Use ${1-} instead of $1 in case 'nounset' is set 
    local -r glob_pattern=${1-} 

    if (($# != 1)) ; then 
     echo 'usage: newest_matching_file GLOB_PATTERN' >&2 
     return 1 
    fi 

    # To avoid printing garbage if no files match the pattern, set 
    # 'nullglob' if necessary 
    local -i need_to_unset_nullglob=0 
    if [[ ":$BASHOPTS:" != *:nullglob:* ]] ; then 
     shopt -s nullglob 
     need_to_unset_nullglob=1 
    fi 

    newest_file= 
    for file in $glob_pattern ; do 
     [[ -z $newest_file || $file -nt $newest_file ]] \ 
      && newest_file=$file 
    done 

    # To avoid unexpected behaviour elsewhere, unset nullglob if it was 
    # set by this function 
    ((need_to_unset_nullglob)) && shopt -u nullglob 

    # Use printf instead of echo in case the file name begins with '-' 
    [[ -n $newest_file ]] && printf '%s\n' "$newest_file" 

    return 0 
} 

Używa tylko wbudowanych Bashów i powinna obsługiwać pliki, których nazwy zawierają znaki nowej linii lub inne nietypowe znaki.

+0

Możesz użyć 'nullglob_shopt = $ (shopt -p nullglob)', a następnie '$ nullglob', aby przywrócić' nullglob' jak było wcześniej. –

+0

Sugestia @gniourf_gniourf do użycia $ (shopt -p nullglob) jest dobra. Ogólnie staram się unikać używania podstawiania poleceń ('$()' lub zwrotów), ponieważ jest powolny, szczególnie pod Cygwin, nawet jeśli polecenie używa tylko wbudowanych. Ponadto kontekst podpowłoki, w którym uruchamiane są komendy, może czasami powodować ich zachowanie w nieoczekiwany sposób. Staram się również unikać przechowywania poleceń w zmiennych (jak 'nullglob_shopt'), ponieważ bardzo złe rzeczy mogą się zdarzyć, jeśli wartość zmiennej jest błędna. – pjh

+0

Doceniam dbałość o szczegóły, które mogą doprowadzić do niejasnej awarii, gdy zostanie ona pominięta. Dzięki! –

2

Istnieje o wiele bardziej skuteczny sposób osiągnięcia tego. Rozważmy następującą komendę:

find . -cmin 1 -name "b2*" 

To polecenie znajdzie najnowszy plik wyprodukowany dokładnie jedną minutę temu z wieloznacznego wyszukiwania w „B2 *”. Jeśli chcesz mieć pliki z ostatnich dwóch dni, lepiej będzie użyć poniższego polecenia:

find . -mtime 2 -name "b2*" 

"." reprezentuje bieżący katalog. Mam nadzieję, że to pomoże.

+5

Nie znajduje to "najnowszego wzorca dopasowania pliku" ... po prostu znajduje wszystkie pliki pasujące do wzorca utworzonego minutę temu lub zmodyfikowane dwa dni temu. – GnP

+0

Ta odpowiedź była oparta na zadanym pytaniu. Możesz także dostosować polecenie, aby obejrzeć najnowszy plik, który pojawił się mniej więcej dzień wcześniej. To zależy od tego, co próbujesz zrobić. – Naufal

2

Połączenie find i ls działa dobrze dla

  • nazwach bez nowej linii
  • niezbyt dużą ilością plików
  • niezbyt długie nazwy plików

Rozwiązanie:

find . -name "my-pattern" ... -print | 
    xargs -0 ls -1 -t | 
    head -1 

Złammy go:

Z find możemy dopasować wszystkie interesujące pliki tak:

find . -name "my-pattern" ... 

następnie używając -print0 możemy przekazać wszystkie nazwy plików bezpiecznie do ls tak:

find . -name "my-pattern" ... -print0 | xargs -0 ls -1 -t 

ls -t posortuje pliki według czasu modyfikacji (najnowszy pierwszy) i wydrukuj go w wierszu. Możesz użyć -c do sortowania według czasu utworzenia. Uwaga: spowoduje to zerwanie nazw plików zawierających znaki nowej linii.

Wreszcie head -1 dostaje nam pierwszy plik na posortowanej liście.

Uwaga:xargs stosuje ograniczenia systemowe do rozmiaru listy argumentów. Jeśli rozmiar ten przekroczy, xargs zadzwoni pod numer ls wiele razy. Spowoduje to przerwanie sortowania i prawdopodobnie także ostatecznego wyniku. Przeprowadź

xargs --show-limits 

, aby sprawdzić limity w swoim systemie.

Powiązane problemy