bir dizindeki en son dosyayı nasıl kuyruklayabilirim


20

Kabukta, tailbir dizinde oluşturulan en son dosyayı nasıl oluşturabilirim ?


1
yakınlaşmak, programcılar kuyruk gerekir!
amit

Kapat yalnızca süper kullanıcı veya sunucu hatasına geçmek içindir. Soru orada yaşayacak ve ilgilenebilecek daha fazla kişi bunu bulacak.
Mart'ta Mnementh

Buradaki asıl sorun dizindeki en son güncelleme dosyasını bulmak ve bunun zaten cevaplanmış olduğuna inanıyorum (burada veya Süper Kullanıcıda hatırlayamıyorum).
dmckee

Yanıtlar:


24

Do not ls çıkışını ayrıştırmak! Ls çıktısını ayrıştırmak zor ve güvenilir değildir .

Bunu yapmanız gerekiyorsa find kullanmanızı öneririz. Başlangıçta burada size sadece çözümün özünü vermek için basit bir örnek vardı, ancak bu cevap biraz popüler göründüğünden, kopyalamak / yapıştırmak ve tüm girdilerle kullanmak için güvenli bir sürüm sağlamak için bunu gözden geçirmeye karar verdim. Rahat oturuyor musun? Geçerli dizindeki en son dosyayı verecek bir oneliner ile başlayacağız:

tail -- "$(find . -maxdepth 1 -type f -printf '%T@.%p\0' | sort -znr -t. -k1,2 | while IFS= read -r -d '' -r record ; do printf '%s' "$record" | cut -d. -f3- ; break ; done)"

Şu anda bir oneliner değil, değil mi? Burada yine bir kabuk işlevi olarak bulunur ve daha kolay okuma için biçimlendirilir:

latest-file-in-directory () {
    find "${@:-.}" -maxdepth 1 -type f -printf '%T@.%p\0' | \
            sort -znr -t. -k1,2 | \
            while IFS= read -r -d '' -r record ; do
                    printf '%s' "$record" | cut -d. -f3-
                    break
            done
}

Ve şimdi o bir Oneliner olarak:

tail -- "$(latest-file-in-directory)"

Eğer her şey başarısız olursa, yukarıdaki işlevi kendi .bashrcbaşınıza dahil edebilir ve sorunun bir uyarı ile çözüldüğünü düşünebilirsiniz. Eğer işi bitirmek istiyorsanız daha fazla okumaya gerek yok.

Bununla ilgili uyarı, bir veya daha fazla yeni satırla biten bir dosya adının hala taildoğru bir şekilde geçirilmemesidir . Bu soruna geçici bir çözüm bulmak ve bu tür kötü amaçlı bir dosya adı ile karşılaşılırsa daha tehlikeli bir şey yerine "böyle bir dosya yok" hatasıyla karşılaşma nispeten güvenli davranışı yeterli olacağını düşünüyorum.

Sulu detaylar

Merak ediyorsanız, bu nasıl çalıştığı, neden güvenli olduğu ve diğer yöntemlerin muhtemelen neden olmadığının yorucu açıklamasıdır.

Tehlike, Will Robinson

Her şeyden önce, dosya yollarını sınırlamak için güvenli olan tek bayt boştur, çünkü Unix sistemlerindeki dosya yollarında evrensel olarak yasaklanmış tek bayttır. Herhangi bir dosya yolu listesini işlerken null değerini yalnızca sınırlayıcı olarak kullanmak ve tek bir dosya yolunu bir programdan diğerine verirken, bunu keyfi baytları boğmayacak şekilde yapmak önemlidir. Bunu ve dosya adlarının içinde yeni satırlar veya boşluklar olmayacağını varsayarak (yanlışlıkla) başarısız olan bu ve diğer sorunları çözmenin görünüşte doğru birçok yolu vardır. Her iki varsayım da güvenli değildir.

Bugünün amaçları için birinci adım, dosyaların sınırsız bir listesini bulmaktır. findGNU'lar -print0gibi bir desteğiniz varsa bu oldukça kolaydır :

find . -print0

Ancak bu liste hala hangisinin en yeni olduğunu söylemiyor, bu yüzden bu bilgileri eklememiz gerekiyor. -printfÇıktıda hangi verilerin görüneceğini belirlememe izin veren find anahtarını kullanmayı seçiyorum . Tüm finddestek sürümleri -printf(standart değildir) değil, GNU bulgusu da destekliyor . Kendiniz olmadan -printfbulursanız, standart olmayan -exec stat {} \;tüm taşınabilirlik umudundan vazgeçmelisiniz stat. Şimdilik GNU araçlarınız olduğunu varsayacağım.

find . -printf '%T@.%p\0'

Burada %T@Unix döneminin başlangıcından bu yana saniye cinsinden modifikasyon süresi olan printf formatını ve ardından bir periyodu izleyen bir sayı izliyorum. Bu başka bir noktaya ekleyin ve sonra %pboş bir bayt ile bitmeden önce (dosyanın tam yoludur).

Şimdi sahibim

find . -maxdepth 1 \! -type d -printf '%T@.%p\0'

Söylemeden geçebilir, ancak tam olması uğruna , alt dizinlerin içeriğini listelemeyi ve istemediğiniz olası dizinleri atlamayı -maxdepth 1engeller . Şimdiye kadar geçerli dizinde değişiklik zamanı bilgileri içeren dosyalar var, bu yüzden şimdi bu değişiklik süresine göre sıralamam gerekiyor.find\! -type dtail

Doğru sırayla almak

Varsayılan olarak sort, girdisinin yeni satırla ayrılmış kayıtlar olmasını bekler. GNU'nuz sortvarsa, -zanahtarı kullanarak boş sınırlandırılmış kayıtlar beklemesini isteyebilirsiniz .; standart için sortçözüm yoktur. Sadece ilk iki sayı (saniye ve saniyenin kesirleri) sıralama ile ilgileniyorum ve gerçek dosya adına göre sıralamak istemiyorum, bu yüzden sortiki şey söylüyorum : İlk olarak, nokta ( .) bir alan sınırlayıcı düşünmelisiniz ve ikincisi, kayıtların nasıl sıralanacağını düşünürken yalnızca birinci ve ikinci alanları kullanmalıdır.

| sort -znr -t. -k1,2

Her şeyden önce birlikte değer almayan üç kısa seçenek topluyorum; -znrsadece kısa ve öz bir söylemdir -z -n -r). Bundan sonra -t .(boşluk isteğe bağlıdır) sortalan sınırlayıcı karakterini söyler ve -k 1,2alan sayılarını belirtir: birinci ve ikinci ( sortsıfırdan değil, bir alandan sayar). Geçerli dizin için örnek bir kaydın şöyle görüneceğini unutmayın:

1000000000.0000000000../some-file-name

Bu sort, önce 1000000000ve sonra 0000000000bu kaydı sipariş ederken bakılacağı anlamına gelir . Her iki değer de sayı olduğundan, -nseçenek sortbu değerleri karşılaştırırken sayısal karşılaştırmayı kullanmayı söyler . Bu önemli olmayabilir, çünkü sayılar sabit uzunluktadır, ancak zarar vermez.

Verilen diğer anahtar sortolduğu -riçin "ters". Varsayılan olarak, sayısal bir sıralamanın çıktısı önce en düşük sayılar -rolur, en son sayıları ve ilk önce en yüksek sayıları listeleyecek şekilde değiştirir. Bu sayılar zaman damgaları daha yüksek olduğu için daha yeni anlamına gelir ve bu listenin başına en yeni kaydı koyar.

Sadece önemli parçalar

Dosya yollarının listesi sortondan çıktığı için şimdi istediğimiz cevabı buluyoruz. Geriye kalan diğer kayıtları atmanın ve zaman damgasını çıkarmanın bir yolunu bulmaktır. Maalesef GNU headve tailboş değerli girişlerde çalışmasını sağlamak için anahtarları kabul etmiyoruz. Bunun yerine bir tür zavallı adamı bir tür zavallı adam olarak kullanıyorum head.

| while IFS= read -r -d '' record

Önce IFSdosya listesi kelime bölme tabi olmayacak şekilde ayarlanmamış . Sonra readiki şey söylüyorum : input ( -r) içindeki escape dizilerini yorumlamayın ve girdi null byte ( -d) ile sınırlandırılır ; burada boş dize '', null ile sınırlandırılmış "sınırlayıcı yok" olarak belirtilir. Her kayıt değişkene okunur, recordböylece whiledöngü her yinelediğinde tek bir zaman damgası ve tek bir dosya adı olur. Bunun -dbir GNU uzantısı olduğuna dikkat edin ; sadece bir standardınız varsa readbu teknik işe yaramaz ve çok az başvurunuz olur.

recordDeğişkenin üç bölümden oluştuğunu biliyoruz , hepsi nokta karakterleriyle sınırlandırılmış. cutYardımcı programı kullanarak bir kısmını çıkarmak mümkündür.

printf '%s' "$record" | cut -d. -f3-

Burada tüm kayıt printforaya ve oradan geçilir cut; bash Bir kullanarak bu daha da basitleştirmek olabilir burada dize kadar cut -d. -3f- <<<"$record"iyi performans için. cutİki şey söyleriz : Birincisi, -dalanları tanımlamak için belirli bir sınırlayıcı olmalıdır ( sortsınırlayıcıda .olduğu gibi). İkincisi cut, -fyalnızca belirli alanlardan değerleri yazdırmak için talimat verilir ; alan listesi, 3-üçüncü alandan ve sonraki tüm alanlardan değeri gösteren bir aralık olarak verilir . Bu , kayıtta bulduğu cutikinciye kadar olan her şeyi okuyacak ve yok sayacak .ve daha sonra dosya yolu kısmı olan geri kalanını yazdıracağı anlamına gelir.

En yeni dosya yolunu yazdırdıktan sonra devam etmenize gerek yoktur: breakikinci dosya yoluna geçmesine izin vermeden döngüden çıkar.

Geriye kalan tek şey, tailbu ardışık düzen tarafından döndürülen dosya yolunda çalışıyor . Örneğimde, boru hattını bir alt kabuk içine alarak bunu yaptığımı fark etmiş olabilirsiniz; fark etmemiş olabilirsiniz, alt kabuğu çift tırnak içine aldım. Bu önemlidir, çünkü sonunda tüm bu çabalarla bile herhangi bir dosya adı için güvenli olmak, sıralanmamış bir alt kabuk genişletmesi yine de bazı şeyleri kırabilir. Bir daha ayrıntılı açıklama eğer ilgilenirsen mevcuttur. İkinci önemli ama kolayca gözden kaçan yönü , dosya adını genişletmeden önce ona tailseçenek --sağladığımdır. Bu talimat verecektaildaha fazla seçeneğin belirtilmediğini ve aşağıdaki her şeyin bir dosya adı olduğunu ve bu da ile başlayan dosya adlarını işlemeyi güvenli kıldığını unutmayın -.


1
@AakashM: çünkü bir dosya adında "olağandışı" karakterler varsa (neredeyse tüm karakterler yasaldır) "şaşırtıcı" sonuçlar alabilirsiniz.
John Zwinck

6
Dosya adlarında özel karakterler kullanan kişiler, aldıkları her şeyi hak ediyor :-)

6
Paxdiablo'yu görmek, sözün yeterince acı vermesini sağladı, ancak iki kişi oy verdi! Buggy yazılımı yazan insanlar kasten aldıkları her şeyi hak ediyorlar.
John Zwinck

4
Bu nedenle yukarıdaki çözüm, bulmada -printf seçeneğinin bulunmaması nedeniyle osx üzerinde çalışmaz, ancak aşağıdakiler yalnızca stat komutundaki farklılıklar nedeniyle osx üzerinde çalışır ... belki yine de birine yardımcı olacaktırtail -f $(find . -type f -exec stat -f "%m {}" {} \;| sort -n | tail -n 1 | cut -d ' ' -f 2)
audio.zoom

2
"Maalesef GNU headve tailboş değerli girişlerde çalışmasını sağlamak için anahtarları kabul etmiyoruz." Benim yedek head: … | grep -zm <number> "".
Kamil Maciorowski

22
tail `ls -t | head -1`

Boşluklu dosya adları konusunda endişeleniyorsanız,

tail "`ls -t | head -1`"

1
Ancak en son dosyanızda boşluklar veya özel karakterler olduğunda ne olur? `` Yerine $ () kullanın ve bu sorunu önlemek için alt kabuğunuzu belirtin.
Mart'ta phogg

Bunu severim. Temiz ve basit. Olması gerektiği gibi.

6
Sağlam ve doğru kurban ederseniz, temiz ve basit olmak kolaydır.
Mart'ta phogg

2
Gerçekten ne yaptığınıza bağlı. Her yerde, olası tüm dosya adları için her zaman çalışan bir çözüm çok güzel, ancak kısıtlı bir durumda (örneğin, garip olmayan isimlerle günlük dosyaları) gereksiz olabilir.

Bu şimdiye kadarki en temiz çözüm. Teşekkür ederim!
demisx

4

Kullanabilirsiniz:

tail $(ls -1t | head -1)

$()Yapı komutu çalışan bir alt kabuk başlatır ls -1tolduğunu içinden ve boru (her zaman sırayla dosyaları, satır başına bir listeleme) head -1ilk satırı (dosya) alır.

Bu komutun çıktısı (en son dosya) tailişlenmek üzere aktarılır .

Bu, en son oluşturulan dizin girdisi ise dizin alma riskini taşıdığını unutmayın. Bir takma adda yalnızca bu günlük dosyalarını içeren bir dizindeki en son günlük dosyasını (dönen bir kümeden) düzenlemek için kullandım.


-1Gerekli değil, lsbu bir boruda olduğunda sizin için bunu yapar. Karşılaştır lsve ls|catörneğin.
sonraki duyuruya kadar duraklatıldı.

Linux altında durum böyle olabilir. "True" Unix'te süreçler, çıktılarının nereye gittiğine bağlı olarak davranışlarını değiştirmedi. Bu boru hattı hata ayıklama gerçekten can sıkıcı :-)

Hmmm, bu doğru değil emin - ISTR bir filtre yoluyla çıkış borulama yaparken 4.2BSD altında sütun biçimli çıktı almak için "ls -C" vermek zorunda ve Solaris altında ls aynı şekilde çalıştığından eminim. Zaten "bir, gerçek Unix" nedir?

Alıntılar! Alıntılar! Dosya adlarında boşluklar var!
Norman Ramsey

@TMN: Gerçek bir Unix yolu, insan olmayan tüketiciler için ls'ye güvenmemek. "Çıktı bir terminale ise, biçim uygulama tanımlıdır." - bu özellik. Eğer emin olmak istiyorsanız ls -1 veya ls -C demek zorundasınız.
Mart'ta phogg

4

POSIX sistemlerinde, "son oluşturulan" dizin girişini almanın bir yolu yoktur. Her dizin girdisi vardır atime, mtimeve ctimefakat Microsoft Windows aksine, ctime"Son durum değişikliği Zamanı" ortalama CreationTime yapar değil ama.

Bu yüzden elde edebileceğiniz en iyi şey, diğer yanıtlarda açıklanan "son değiştirilen dosyayı kuyruğa almak" tır. Bu komut için gitmek istiyorum:

kuyruk -f "$ (ls -tr | sed 1q)"

lsKomutun etrafındaki tırnaklara dikkat edin . Bu, snippet'in neredeyse tüm dosya adlarıyla çalışmasını sağlar.


İyi iş. Noktasına doğru. +1
Norman Ramsey

4

Sadece izlemek için kullanabileceğiniz dosya boyutu değişikliğini görmek istiyorum.

watch -d ls -l

3

İçinde zsh:

tail *(.om[1])

Bkz. Http://zsh.sourceforge.net/Doc/Release/Expansion.html#Glob-Qualifiers , burada mdeğişiklik süresini gösterir m[Mwhms][-|+]nve önceki oyöntem bir şekilde sıralandığı anlamına gelir ( Obaşka bir şekilde sıralar). .Tek yolu düzenli dosyaları. Parantez içinde [1]ilk öğeyi alır. Üç kullanım seçmek [1,3]için, en eski kullanımı elde etmek için [-1].

Güzel kısa ve kullanmaz ls.


1

Muhtemelen bunu yapmanın bir milyon yolu vardır, ama bunu yapmamın yolu şudur:

tail `ls -t | head -n 1`

Ters çentikler (alıntı benzeri karakterler) arasındaki bitler yorumlanır ve sonuç kuyruğa geri döner.

ls -t #gets the list of files in time order
head -n 1 # returns the first line only

2
Backticks kötüdür. Bunun yerine $ () kullanın.
William Pursell

1

Basit:

tail -f /path/to/directory/*

benim için iyi çalışıyor.

Sorun, tail komutunu başlattıktan sonra oluşturulan dosyaları almaktır. Ancak buna ihtiyacınız yoksa (yukarıdaki tüm çözümler umursamadığı gibi), yıldız işareti sadece daha basit bir çözümdür, IMO.



0

Birisi onu gönderdi ve sonra bir nedenden dolayı sildi, ancak çalışan tek kişi bu, yani ...

tail -f `ls -tr | tail`

dizinleri hariç tutmalısın, değil mi?
amit

1
Bunu ilk olarak yayınladım ama Sorpigal ile çıktıyı ayrıştırmanın lsyapılacak en akıllıca şey olmadığını kabul ettiğim için sildim ...
ChristopheD

Hızlı ve kirli ihtiyacım var, içinde dizin yok. Cevabınızı eklerseniz, bunu kabul edeceğim
Itay Moav -Malimovka

0
tail -f `ls -lt | grep -v ^d | head -2 | tail -1 | tr -s " " | cut -f 8 -d " "`

Açıklama:

  • ls -lt: Değişiklik süresine göre sıralanmış tüm dosya ve dizinlerin listesi
  • grep -v ^ d: dizinleri hariç tut
  • kafa -2 sonrası: gerekli dosya adını ayrıştırma

1
Zeki için +1, ls çıktısını ayrıştırma için -2, alt kabuğu alıntılamama için -1, sihirli bir "alan 8" varsayımı için -1 (taşınabilir değil!) Ve son olarak da çok zeki için -1 . Toplam puan: -4.
Mart'ta phogg

@Sorpigal Anlaştı. Kötü örnek olmaktan mutluluk duyuyorum.
amit

evet o çok sayıları yanlış olacağını hayal vermedi
Amit

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.