En yeni dosya eşleme desenini bulmak için Bash işlevi


141

Bash'te, belirli bir kalıpla eşleşen en yeni dosyanın dosya adını döndüren bir işlev oluşturmak istiyorum. Örneğin, aşağıdaki gibi bir dosya dizini var:

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

'B2' ile başlayan en yeni dosyayı istiyorum. Bunu bash'da nasıl yaparım? Senaryomda buna ihtiyacım var ~/.bash_profile.


4
daha fazla cevap ipucu için bkz. superuser.com/questions/294161/… . Sıralama en yeni dosyayı almak için önemli bir adımdır
Wolfgang Fahl

Yanıtlar:


229

lsKomutu bir parametresi vardır -tzaman sıralamak. Daha sonra ilk (en yeni) ile yakalayabilirsiniz head -1.

ls -t b2* | head -1

Ama dikkat: Neden ls çıktısını ayrıştırmamalısınız

Kişisel fikrim: Ayrıştırma lsyalnızca dosya adları boşluk veya satırsonu gibi komik karakterler içerebiliyorsa tehlikelidir. Dosya adlarının komik karakterler içermeyeceğini garanti ediyorsanız, ayrıştırma lsoldukça güvenlidir.

Birçok farklı durumlarda pek sistemlerde birçok kişi tarafından çalıştırılacak amaçlanan bir senaryoyu geliştiriyorsanız sonra ben çok ayrıştırmak değil tavsiye edersiniz ls.

İşte nasıl "doğru": Bir dizindeki en son (en yeni, en eski, en eski) dosyayı nasıl bulabilirim?

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

8
Diğerlerine not: bir dizin için bunu yapıyorsanız, ls için -d seçeneğini eklersiniz, şöyle 'ls -td <pattern> | kafa -1 '
ken.ganong

5
Ayrıştırma LS bağlantı bu yapmamaya diyor ve yöntemleri önermektedir BashFAQ 99 . Bir senaryoya dahil etmek için kurşun geçirmez bir şey yerine 1-liner arıyorum, bu yüzden ls'yi @lesmana gibi güvensiz bir şekilde ayrıştırmaya devam edeceğim.
İsimsiz

1
@Eponymous: Kırılgan kullanmadan bir astar arıyorsanız ls, printf "%s\n" b2* | head -1bunu sizin için yapar.
David Ongaro

2
@DavidOngaro Soru, dosya adlarının sürüm numaraları olduğunu söylemiyor. Bu değişiklik süreleriyle ilgilidir. Dosya adı varsayımı ile bile b2.10_5_2bu çözümü öldürür.
İsimsiz

1
Tek astarınız bana doğru cevabı veriyor, ama "doğru" yol aslında bana en eski dosyayı veriyor . Neden olduğu hakkında bir fikrin var mı?
NewNameStat

15

Kombinasyonu findve lsiçin iyi çalışır

  • yeni satır içermeyen dosya adları
  • çok fazla dosya değil
  • çok uzun dosya adları değil

Çözüm:

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

Hadi yıkalım:

İle findbunun gibi tüm ilginç dosyaları eşleştirebiliriz:

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

sonra kullanarak -print0tüm dosya adlarını güvenli bir şekilde şu şekilde aktarabiliriz ls:

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

findburaya ek arama parametreleri ve kalıpları eklenebilir

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

ls -tdosyaları değiştirme zamanına (en yenisi) göre sıralar ve bir satıra bir tane basar. -cOluşturma zamanına göre sıralamak için kullanabilirsiniz . Not : bu, yeni satırlar içeren dosya adlarıyla kesilir.

Sonunda head -1bize sıralanmış listedeki ilk dosyayı alır.

Not: xargs bağımsız değişken listesinin boyutuyla ilgili sistem sınırlarını kullanın. Bu boyut aşarsa, xargsarayacak lsbirden çok kez. Bu sıralama ve muhtemelen nihai çıktıyı kıracaktır. Çalıştırmak

xargs  --show-limits

sisteminizdeki sınırları kontrol etmek için.

Not 2: kullanım find . -maxdepth 1 -name "my-pattern" -print0alt klasörlere üzerinden dosya aramak istemiyorsanız.

Not 3: @starfry tarafından işaret edildiği gibi - için -rargüman xargsçağrılmasını engelliyorsa, ls -1 -tile eşleşen dosya yoksa find. Öneri için teşekkürler.


2
Bu ls tabanlı çözümlerden daha iyidir, çünkü ls boğulduğu son derece fazla dosya içeren dizinler için çalışır.
Marcin Zukowski

find . -name "my-pattern" ... -print0bana verirfind: paths must precede expression: `...'
Jaakko

Ah! ..."daha fazla parametre" anlamına gelir. İhtiyacınız yoksa, atlayın.
Boris Brodski

2
Desenle eşleşen dosya yoksa, bu kalıbın eşleşmeyen bir dosyayı döndürebildiğini buldum. Bunun nedeni, find'ın xargs'a hiçbir şey iletmemesi, bu nedenle daha sonra dosya listesi olmadan ls'yi çağırması ve tüm dosyalarda çalışmasına neden olmasıdır. Çözüm, -rxargs komut satırına eklenerek xargs'a standart girdisinde hiçbir şey almazsa komut satırını çalıştırmamalarını söyler.
starfry

@starfry teşekkür ederim! İyi yakaladın. Cevaba ekledim -r.
Boris Brodski

7

Bu, gerekli Bash işlevinin olası bir uygulamasıdır:

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

Yalnızca Bash yerleşiklerini kullanır ve adları yeni satırlar veya olağandışı karakterler içeren dosyaları işlemelidir.


1
Daha nullglob_shopt=$(shopt -p nullglob)sonra nasıl olduğunu $nullglobgeri koymak için kullanabilirsiniz nullglob.
gniourf_gniourf

@Gniourf_gniourf tarafından $ (shopt -p nullglob) kullanılması önerisi iyi bir şeydir. Genellikle komut ikamesi ( $()veya backticks) kullanmaktan kaçınmaya çalışırım, çünkü komut sadece yerleşikleri kullanıyor olsa bile, özellikle Cygwin altında yavaştır. Ayrıca, komutların çalıştırıldığı alt kabuk içeriği bazen beklenmedik şekillerde davranmalarına neden olabilir. Ayrıca değişkenlerin (gibi nullglob_shopt) komutları saklamaktan kaçınmaya çalışıyorum, çünkü değişkenin değerini yanlış alırsanız çok kötü şeyler olabilir.
pjh

Göz ardı edildiğinde belirsiz bir başarısızlığa yol açabilecek ayrıntılara gösterilen dikkati takdir ediyorum. Teşekkürler!
Ron Burk

Sorunu çözmek için daha eşsiz bir yol izlemenizi seviyorum! Unix / Linux'ta 'kaplamak cat!' İçin birden fazla yol olduğu kesindir . Bu daha fazla iş alsa bile insanlara kavramları gösterme avantajına sahiptir. +1 alın!
13'te Pryftan

3

Olağandışı dosya adları (geçerli \nkarakteri içeren bir dosya gibi bu ayrıştırma işlemine zarar verebilir. Perl'de yapmanın bir yolu:

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

Orada kullanılan bir Schwartz dönüşümü .


1
Schwartz seninle olsun!
Nathan Monteleone

Bu cevap işe yarayabilir, ancak kötü belgeler verilen güven olmaz.
Wolfgang Fahl

1

Sen kullanabilirsiniz statbir dosya topak ve ön eklenen dosya zamanla bir süslemeleri-sort-undecorate ile:

$ stat -f "%m%t%N" b2* | sort -rn | head -1 | cut -f2-

Hayır. "stat: '% m% t% N' için dosya sistemi bilgilerini okuyamıyor: Böyle bir dosya veya dizin yok"
Ken Ingram

Ben statonun seçeneklerini doğru hatırlıyorum, bu Mac / FreeBSD sürümü için olabilir düşünüyorum . Diğer platformlarda benzer çıktılar almak için aşağıdakileri kullanabilirsinizstat -c $'%Y\t%n' b2* | sort -rn | head -n1 | cut -f2-
Jeffrey Cash

1

find ... xargs ... head ...Yukarıdaki çözümü isteyenler için kara büyü işlevi büyütme , ancak kullanımı kolay işlev formunda, düşünmenize gerek yok:

#define the function
find_newest_file_matching_pattern_under_directory(){
    echo $(find $1 -name $2 -print0 | xargs -0 ls -1 -t | head -1)
}

#setup:
#mkdir /tmp/files_to_move
#cd /tmp/files_to_move
#touch file1.txt
#touch file2.txt

#invoke the function:
newest_file=$( find_newest_file_matching_pattern_under_directory /tmp/files_to_move/ bc* )
echo $newest_file

Baskılar:

file2.txt

Hangisi:

Belirtilen dizinin altındaki belirli bir desenle eşleşen dosyanın en eski değiştirilmiş zaman damgasına sahip dosya adı.


1

Find komutunu kullanın.

Bash 4.2+ kullandığınızı varsayarsak, -printf '%T+ %p\n'dosya zaman damgası değeri için kullanın .

find $DIR -type f -printf '%T+ %p\n' | sort -r | head -n 1 | cut -d' ' -f2

Misal:

find ~/Downloads -type f -printf '%T+ %p\n' | sort -r | head -n 1 | cut -d' ' -f2

Daha kullanışlı bir komut dosyası için buradaki en yeni komut dosyasına bakın : https://github.com/l3x/helpers


boşluk içeren dosya adları ile çalışmak nasıl cut -d '' -f2,3,4,5,6,7,8,9 ...
valodzka

0

Bunu başarmanın çok daha etkili bir yolu var. Aşağıdaki komutu göz önünde bulundurun:

find . -cmin 1 -name "b2*"

Bu komut, tam olarak bir dakika önce üretilen en son dosyayı "b2 *" üzerindeki joker karakter aramasıyla bulur. Son iki günün dosyalarını istiyorsanız, aşağıdaki komutu kullanarak daha iyi durumda olursunuz:

find . -mtime 2 -name "b2*"

"." geçerli dizini temsil eder. Bu yardımcı olur umarım.


9
Bu aslında "en yeni dosya eşleme desenini" bulamaz ... sadece bir dakika önce oluşturulan ya da iki gün önce değiştirilen desenle eşleşen tüm dosyaları bulur.
GnP

Bu cevap sorulan soruya dayanıyordu. Ayrıca, bir gün önce gelen en son dosyaya bakmak için komutu düzenleyebilirsiniz. Ne yapmaya çalıştığınıza bağlıdır.
Naufal

"tweaking" cevabı değil. bu bir cevap olarak yayınlamak gibidir: "find komutunu değiştirin ve ne yapmak istediğinize bağlı olarak cevabı bulun".
Kennet Celeste

Gereksiz yorum hakkında emin değilim. Cevabımın doğrulanmadığını düşünüyorsanız, lütfen cevabımın neden ÖRNEKLER ile anlam ifade etmediğine uygun bir neden belirtin. Bunu yapamıyorsanız, lütfen daha fazla yorum yapmaktan kaçının.
Naufal

1
Çözümünüz, en son dosyanın ne zaman oluşturulduğunu bilmenizi gerektirir . Bu soruda değildi, yani hayır, cevabınız sorulan soruya dayanmıyor.
Pub Bloke Down
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.