Kabukta, tailbir dizinde oluşturulan en son dosyayı nasıl oluşturabilirim ?
Kabukta, tailbir dizinde oluşturulan en son dosyayı nasıl oluşturabilirim ?
Yanıtlar:
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.
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.
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
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.
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 -.
tail -f $(find . -type f -exec stat -f "%m {}" {} \;| sort -n | tail -n 1 | cut -d ' ' -f 2)
headve tailboş değerli girişlerde çalışmasını sağlamak için anahtarları kabul etmiyoruz." Benim yedek head: … | grep -zm <number> "".
tail `ls -t | head -1`
Boşluklu dosya adları konusunda endişeleniyorsanız,
tail "`ls -t | head -1`"
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.
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.
İç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.
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
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.
Birisi onu gönderdi ve sonra bir nedenden dolayı sildi, ancak çalışan tek kişi bu, yani ...
tail -f `ls -tr | tail`
lsyapılacak en akıllıca şey olmadığını kabul ettiğim için sildim ...
tail -f `ls -lt | grep -v ^d | head -2 | tail -1 | tr -s " " | cut -f 8 -d " "`
Açıklama: