Kabukta, tail
bir dizinde oluşturulan en son dosyayı nasıl oluşturabilirim ?
Kabukta, tail
bir 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 .bashrc
başı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 tail
doğ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. find
GNU'lar -print0
gibi 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 find
destek sürümleri -printf
(standart değildir) değil, GNU bulgusu da destekliyor . Kendiniz olmadan -printf
bulursanı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 %p
boş 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 1
engeller . Ş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 d
tail
Varsayılan olarak sort
, girdisinin yeni satırla ayrılmış kayıtlar olmasını bekler. GNU'nuz sort
varsa, -z
anahtarı 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 sort
iki ş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; -znr
sadece kısa ve öz bir söylemdir -z -n -r
). Bundan sonra -t .
(boşluk isteğe bağlıdır) sort
alan sınırlayıcı karakterini söyler ve -k 1,2
alan sayılarını belirtir: birinci ve ikinci ( sort
sı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 1000000000
ve sonra 0000000000
bu kaydı sipariş ederken bakılacağı anlamına gelir . Her iki değer de sayı olduğundan, -n
seçenek sort
bu 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 sort
olduğu -r
için "ters". Varsayılan olarak, sayısal bir sıralamanın çıktısı önce en düşük sayılar -r
olur, 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 sort
ondan çı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 head
ve tail
boş 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 IFS
dosya listesi kelime bölme tabi olmayacak şekilde ayarlanmamış . Sonra read
iki ş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, record
böylece while
döngü her yinelediğinde tek bir zaman damgası ve tek bir dosya adı olur. Bunun -d
bir GNU uzantısı olduğuna dikkat edin ; sadece bir standardınız varsa read
bu teknik işe yaramaz ve çok az başvurunuz olur.
record
Değişkenin üç bölümden oluştuğunu biliyoruz , hepsi nokta karakterleriyle sınırlandırılmış. cut
Yardımcı programı kullanarak bir kısmını çıkarmak mümkündür.
printf '%s' "$record" | cut -d. -f3-
Burada tüm kayıt printf
oraya 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, -d
alanları tanımlamak için belirli bir sınırlayıcı olmalıdır ( sort
sınırlayıcıda .
olduğu gibi). İkincisi cut
, -f
yalnı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 cut
ikinciye 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: break
ikinci dosya yoluna geçmesine izin vermeden döngüden çıkar.
Geriye kalan tek şey, tail
bu 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 tail
seçenek --
sağladığımdır. Bu talimat verecektail
daha 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)
head
ve tail
boş 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 -1t
olduğunu içinden ve boru (her zaman sırayla dosyaları, satır başına bir listeleme) head -1
ilk satırı (dosya) alır.
Bu komutun çıktısı (en son dosya) tail
iş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.
-1
Gerekli değil, ls
bu bir boruda olduğunda sizin için bunu yapar. Karşılaştır ls
ve ls|cat
örneğin.
POSIX sistemlerinde, "son oluşturulan" dizin girişini almanın bir yolu yoktur. Her dizin girdisi vardır atime
, mtime
ve ctime
fakat 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)"
ls
Komutun 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 m
değişiklik süresini gösterir m[Mwhms][-|+]n
ve önceki o
yöntem bir şekilde sıralandığı anlamına gelir ( O
baş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`
ls
yapı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: