शेल में, मैं tail
निर्देशिका में नवीनतम फ़ाइल कैसे बना सकता हूं ?
शेल में, मैं tail
निर्देशिका में नवीनतम फ़ाइल कैसे बना सकता हूं ?
जवाबों:
Ls के आउटपुट को पार्स न करें ! Ls के आउटपुट को पार्स करना कठिन और अविश्वसनीय है ।
यदि आपको यह करना चाहिए तो मैं खोज का उपयोग करने की सलाह देता हूं। मूल रूप से मेरे पास यहां एक सरल उदाहरण था जो आपको समाधान का सार देने के लिए था, लेकिन चूंकि यह उत्तर कुछ लोकप्रिय लगता है इसलिए मैंने इसे संशोधित करने का फैसला किया जो एक संस्करण प्रदान करने के लिए सुरक्षित है जो सभी इनपुटों के साथ कॉपी / पेस्ट और उपयोग करने के लिए सुरक्षित है। क्या आप आराम से बैठे हैं? हम एक ऑनलाइनर से शुरू करेंगे जो आपको वर्तमान निर्देशिका में नवीनतम फ़ाइल देगा:
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)"
अब एक बहुत oneliner नहीं है? यहाँ यह फिर से एक शेल फ़ंक्शन के रूप में है और आसान पढ़ने के लिए स्वरूपित किया गया है:
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
}
और अब है कि एक oneliner के रूप में:
tail -- "$(latest-file-in-directory)"
यदि अन्य सभी विफल रहता है, तो आप उपरोक्त फ़ंक्शन को अपने में शामिल कर सकते हैं .bashrc
और समस्या को हल कर सकते हैं , एक कैवेट के साथ। यदि आप सिर्फ काम पूरा करना चाहते हैं तो आपको आगे पढ़ने की आवश्यकता नहीं है।
इसके साथ चेतावनी यह है कि एक या एक से अधिक newlines में समाप्त होने वाला फ़ाइल नाम अभी भी tail
सही तरीके से पारित नहीं किया जाएगा । इस समस्या के आसपास काम करना जटिल है और मैं इसे पर्याप्त मानता हूं कि अगर इस तरह के दुर्भावनापूर्ण फ़ाइल नाम का सामना करने में अपेक्षाकृत सुरक्षित व्यवहार का सामना करना पड़ता है, तो "नो ऐसी फाइल" त्रुटि अधिक खतरनाक होने के बजाय घटित होगी।
जिज्ञासु के लिए यह थकाऊ विवरण है कि यह कैसे काम करता है, यह सुरक्षित क्यों है और अन्य तरीके शायद क्यों नहीं हैं।
सबसे पहले, एकमात्र बाइट जो फ़ाइल पथों को सीमांकित करने के लिए सुरक्षित है, अशक्त है क्योंकि यह यूनिक्स सिस्टम पर फ़ाइल पथों में सार्वभौमिक रूप से निषिद्ध एकमात्र बाइट है। यह महत्वपूर्ण है कि फ़ाइल पथों की किसी भी सूची को केवल एक सीमांकक के रूप में शून्य का उपयोग करने के लिए और, जब एक कार्यक्रम से दूसरे में भी एक ही फ़ाइल पथ को सौंपने के लिए, ऐसा करने के लिए, जो मनमाने ढंग से बाइट्स पर चोक नहीं करेगा। इसे और अन्य समस्याओं को हल करने के लिए कई प्रतीत होते हैं-सही तरीके हैं जो यह मानकर (यहां तक कि गलती से) विफल हो जाते हैं कि फ़ाइल नाम में नई लाइनें या स्थान नहीं होंगे। न तो धारणा सुरक्षित है।
आज के उद्देश्यों के लिए, किसी को खोजने के लिए फ़ाइलों की अशक्त-सीमांकित सूची प्राप्त करना है। यह बहुत आसान है यदि आपके पास एक find
सहायक है -print0
जैसे कि GNU का:
find . -print0
लेकिन यह सूची अभी भी हमें यह नहीं बताती है कि कौन सा सबसे नया है, इसलिए हमें उस जानकारी को शामिल करना होगा। मैं खोज के -printf
स्विच का उपयोग करना चुनता हूं जो मुझे यह निर्दिष्ट करने की अनुमति देता है कि आउटपुट में क्या डेटा दिखाई देता है। find
समर्थन के सभी संस्करण -printf
(यह मानक नहीं है) लेकिन GNU खोज करता है। यदि आप अपने -printf
आप को बिना पाते हैं, तो आपको -exec stat {} \;
इस बात पर भरोसा करने की आवश्यकता होगी कि आपको किस हद तक पोर्टेबिलिटी की सभी आशा छोड़ देनी चाहिए क्योंकि stat
यह मानक भी नहीं है। अभी के लिए मैं आपको GNU टूल मानकर आगे बढ़ने जा रहा हूँ।
find . -printf '%T@.%p\0'
यहां मैं प्रिंटफ प्रारूप के लिए पूछ रहा हूं %T@
जो कि यूनिक्स युग की शुरुआत के बाद से सेकंड में संशोधन का समय है और उसके बाद एक सेकंड के अंशों का संकेत देने वाली संख्या है। मैं एक और अवधि में और फिर %p
(जो फ़ाइल के लिए पूर्ण पथ है) को जोड़ने के लिए एक अशक्त बाइट के साथ समाप्त होने से पहले।
अब मेरे पास है
find . -maxdepth 1 \! -type d -printf '%T@.%p\0'
यह बिना कहे जा सकता है, लेकिन पूरा -maxdepth 1
होने find
के लिए उप निर्देशिका और \! -type d
स्किप निर्देशिका की सामग्री को सूचीबद्ध करने से रोकता है , जिसे आप नहीं चाहते हैं tail
। अब तक मेरे पास वर्तमान निर्देशिका में संशोधन समय की जानकारी के साथ फाइलें हैं, इसलिए अब मुझे उस संशोधन समय के अनुसार क्रमबद्ध करने की आवश्यकता है।
डिफ़ॉल्ट रूप से sort
इसके इनपुट को न्यूलाइन-सीमांकित रिकॉर्ड होने की उम्मीद है। यदि आपके पास जीएनयू है, sort
तो आप -z
स्विच का उपयोग करके इसके बजाय शून्य-सीमांकित रिकॉर्ड की अपेक्षा कर सकते हैं ।; मानक के लिए sort
कोई समाधान नहीं है। मैं केवल पहले दो नंबरों (सेकंड और सेकंड के अंशों) को छांटने में दिलचस्पी रखता हूं और वास्तविक फ़ाइल नाम से सॉर्ट नहीं करना चाहता हूं, इसलिए मैं sort
दो बातें बताता हूं : पहला, यह कि इस अवधि ( .
) को एक फ़ील्ड सीमांकक पर विचार करना चाहिए और दूसरा कि यह केवल पहले और दूसरे क्षेत्र का उपयोग करना चाहिए जब यह विचार करना चाहिए कि अभिलेखों को कैसे छाँटना है।
| sort -znr -t. -k1,2
सबसे पहले मैं तीन छोटे विकल्पों को बंडल कर रहा हूं जो एक साथ कोई मूल्य नहीं लेते हैं; -znr
कहने का एक संक्षिप्त तरीका है -z -n -r
)। उसके बाद -t .
(स्थान वैकल्पिक है) sort
फ़ील्ड को सीमांकक वर्ण बताता है और -k 1,2
फ़ील्ड संख्या निर्दिष्ट करता है: पहले और दूसरे ( sort
शून्य से फ़ील्ड की गणना करता है, शून्य नहीं)। याद रखें कि वर्तमान निर्देशिका के लिए एक नमूना रिकॉर्ड जैसा दिखेगा:
1000000000.0000000000../some-file-name
यह साधन sort
पहले 1000000000
और फिर 0000000000
इस रिकॉर्ड को ऑर्डर करते समय दिखेगा । -n
विकल्प बताता है sort
जब इन मूल्यों की तुलना अंकीय तुलना उपयोग करने के लिए है, क्योंकि दोनों मूल्यों नंबर दिए गए हैं। यह महत्वपूर्ण नहीं हो सकता है क्योंकि संख्या निश्चित लंबाई की है लेकिन इससे कोई नुकसान नहीं है।
दिया गया अन्य स्विच "रिवर्स" के लिए sort
है -r
। डिफ़ॉल्ट रूप से एक संख्यात्मक प्रकार का आउटपुट पहले सबसे कम नंबर होगा, -r
इसे बदल देता है ताकि यह सबसे कम संख्या में अंतिम और सबसे पहले संख्याओं को सूचीबद्ध करे। चूंकि ये संख्याएं टाइमस्टैम्प अधिक हैं, इसलिए इसका मतलब नया होगा और यह सूची की शुरुआत में सबसे नया रिकॉर्ड रखता है।
जैसे-जैसे फ़ाइल पथों की सूची उभरती है, sort
उसमें वांछित उत्तर होता है जिसे हम शीर्ष पर दाईं ओर देख रहे हैं। अन्य अभिलेखों को त्यागने और टाइमस्टैम्प को हटाने का एक तरीका खोजने के लिए क्या शेष है। दुर्भाग्य से यहां तक कि GNU head
और tail
स्विच को स्वीकार नहीं करते हैं ताकि वे अशक्त-सीमांकित इनपुट पर काम कर सकें। इसके बजाय मैं एक तरह के गरीब आदमी के रूप में थोड़ी देर के लूप का उपयोग करता हूं head
।
| while IFS= read -r -d '' record
पहले मैं परेशान हूं IFS
ताकि फाइलों की सूची शब्द विभाजन के अधीन न हो। आगे मैं read
दो बातें बताता हूं : इनपुट ( -r
) में एस्केप अनुक्रमों की व्याख्या न करें और इनपुट को एक शून्य बाइट ( -d
) के साथ सीमांकित किया गया है ; यहां रिक्त स्ट्रिंग ''
का उपयोग नल द्वारा सीमांकित "कोई सीमांकक" उर्फ को इंगित करने के लिए नहीं किया गया है। प्रत्येक रिकॉर्ड को वैरिएबल में पढ़ा जाएगा record
ताकि हर बार while
लूप को पुनरावृत्त करने के लिए एक एकल टाइमस्टैम्प और एक एकल फ़ाइल नाम हो। ध्यान दें कि -d
एक GNU एक्सटेंशन है; यदि आपके पास केवल एक मानक है तो read
यह तकनीक काम नहीं करेगी और आपको थोड़ा संभोग करना होगा।
हम जानते हैं कि record
चर के तीन भाग हैं, सभी अवधि पात्रों द्वारा सीमांकित हैं। cut
उपयोगिता का उपयोग करके उनमें से एक हिस्से को निकालना संभव है।
printf '%s' "$record" | cut -d. -f3-
यहाँ पूरे रिकॉर्ड को पारित किया गया है printf
और वहाँ से पाइप किया गया है cut
; बैश में आप एक का उपयोग कर इस आगे सरल सकता है यहाँ स्ट्रिंग के लिए cut -d. -3f- <<<"$record"
बेहतर प्रदर्शन के लिए। हम cut
दो बातें बताते हैं: पहले इसके साथ -d
खेतों की पहचान के लिए एक विशिष्ट सीमांकक होना चाहिए (जैसा sort
कि सीमांकक .
का उपयोग किया जाता है)। दूसरे cut
को निर्देश दिया जाता है -f
कि वे विशिष्ट क्षेत्रों से केवल मूल्यों को मुद्रित करें; फ़ील्ड सूची को एक श्रेणी के रूप में दिया गया है 3-
जो तीसरे फ़ील्ड से और सभी निम्नलिखित फ़ील्ड से मान इंगित करता है। इसका मतलब यह है कि यह रिकॉर्ड में पाए जाने वाले cut
दूसरे सहित सभी चीजों को पढ़ेगा और अनदेखा .
करेगा और फिर शेष को प्रिंट करेगा, जो कि फाइल डिवाइस भाग है।
नवीनतम फ़ाइल पथ मुद्रित करने के बाद उसे चलते रहने की कोई आवश्यकता नहीं है: break
दूसरी फ़ाइल पथ पर जाने के बिना लूप से बाहर निकलता है।
केवल एक चीज जो बची हुई है tail
, वह इस पाइपलाइन द्वारा लौटाई गई फ़ाइल पथ पर चल रही है । आपने मेरे उदाहरण पर ध्यान दिया होगा कि मैंने यह सबशेल में पाइपलाइन को संलग्न करके किया था; आपने जो नहीं देखा होगा, वह यह है कि मैंने दोहरे उद्धरणों में उपखंड संलग्न किया है। यह महत्वपूर्ण है क्योंकि किसी भी फ़ाइल के लिए सुरक्षित होने के इस प्रयास के साथ अंतिम पर भी एक निर्विरोध उपधारा विस्तार अभी भी चीजों को तोड़ सकता है। एक अधिक विस्तृत विवरण यदि आप रुचि रखते हैं उपलब्ध है। मंगलाचरण के लिए दूसरा महत्वपूर्ण लेकिन आसानी से अनदेखा पहलू यह tail
है कि मैंने --
फ़ाइल नाम का विस्तार करने से पहले इसे विकल्प प्रदान किया । यह निर्देश देगाtail
कि कोई और विकल्प निर्दिष्ट नहीं किया जा रहा है और निम्नलिखित सब कुछ एक फ़ाइल नाम है, जो फ़ाइल नामों को संभालना सुरक्षित बनाता है जो इसके साथ शुरू होते हैं -
।
tail -f $(find . -type f -exec stat -f "%m {}" {} \;| sort -n | tail -n 1 | cut -d ' ' -f 2)
head
और tail
स्विच को स्वीकार नहीं करता है ताकि उन्हें शून्य-सीमांकित इनपुट पर संचालित किया जा सके।" के लिए मेरे प्रतिस्थापन head
: … | grep -zm <number> ""
।
tail `ls -t | head -1`
यदि आप रिक्त स्थान वाले फ़ाइलनाम के बारे में चिंतित हैं,
tail "`ls -t | head -1`"
आप उपयोग कर सकते हैं:
tail $(ls -1t | head -1)
$()
निर्माण एक उप खोल जो आदेश चलाता है शुरू होता है ls -1t
कि के माध्यम से और पाइपिंग (समय के क्रम में सभी फाइलों, प्रति पंक्ति एक लिस्टिंग) head -1
पहली पंक्ति (फाइल) प्राप्त करने के लिए।
उस आदेश (सबसे हाल की फ़ाइल) का आउटपुट तब tail
संसाधित होने के लिए पारित किया जाता है।
ध्यान रखें कि यह एक निर्देशिका प्राप्त करने के जोखिम को चलाता है यदि वह सबसे हालिया निर्देशिका प्रविष्टि बनाई गई हो। मैंने उस ट्रिक का उपयोग एक उपनाम में सबसे हाल की लॉग फ़ाइल (एक घूर्णन सेट से) को एक निर्देशिका में संपादित करने के लिए किया है जिसमें केवल उन लॉग फ़ाइलों को शामिल किया गया है।
-1
आवश्यक नहीं है, ls
यह आपके लिए है जब यह एक पाइप में है। उदाहरण के लिए तुलना करें ls
और ls|cat
।
POSIX सिस्टम पर, "अंतिम निर्मित" निर्देशिका प्रविष्टि प्राप्त करने का कोई तरीका नहीं है। प्रत्येक निर्देशिका प्रविष्टि में Microsoft Windows के विपरीत atime
, mtime
और है ctime
, लेकिन ctime
इसका मतलब क्रिएशनटाइम नहीं है, लेकिन "अंतिम स्थिति परिवर्तन का समय" है।
तो सबसे अच्छा आप प्राप्त कर सकते हैं "पिछले हाल ही में संशोधित फ़ाइल को पूंछें", जिसे अन्य उत्तरों में समझाया गया है। मैं इस कमांड के लिए जाऊंगा:
पूंछ -f "$ (ls -tr | sed 1q)"
ls
कमांड के चारों ओर उद्धरण नोट करें । यह स्निपेट लगभग सभी फ़ाइलनामों के साथ काम करता है।
इन zsh
:
tail *(.om[1])
देखें: http://zsh.sourceforge.net/Doc/Release/Expansion.html#Glob-Qualifiers , यहां m
संशोधन समय को दर्शाता है m[Mwhms][-|+]n
, और पूर्ववर्ती का o
अर्थ है कि यह एक तरह से सॉर्ट किया जाता है ( O
इसे दूसरे तरीके से सॉर्ट करें)। इसका .
मतलब केवल नियमित फाइलें हैं। कोष्ठक के भीतर [1]
पहला आइटम चुनता है। तीन उपयोग लेने के लिए [1,3]
, सबसे पुराना उपयोग प्राप्त करने के लिए [-1]
।
यह छोटा है और उपयोग नहीं करता है ls
।
ऐसा करने के लिए शायद एक लाख तरीके हैं, लेकिन जिस तरह से मैं यह करूंगा वह यह है:
tail `ls -t | head -n 1`
बैकटिक्स (पात्रों की तरह उद्धरण) के बीच की बिट्स की व्याख्या की जाती है और परिणाम पूंछ पर लौट आता है।
ls -t #gets the list of files in time order
head -n 1 # returns the first line only
एक सरल:
tail -f /path/to/directory/*
मेरे लिए ठीक काम करता है।
समस्या उन फ़ाइलों को प्राप्त करने की है जो आपके द्वारा टेल कमांड शुरू करने के बाद उत्पन्न होती हैं। लेकिन अगर आपको इसकी आवश्यकता नहीं है (जैसा कि ऊपर दिए गए सभी समाधान इसकी परवाह नहीं करते हैं), तारांकन सिर्फ सरल समाधान है, IMO।
किसी ने इसे पोस्ट किया, और फिर किसी कारण से इसे मिटा दिया, लेकिन यह केवल एक ही है जो काम करता है, इसलिए ...
tail -f `ls -tr | tail`
ls
सबसे स्मार्ट काम नहीं है ...
tail -f `ls -lt | grep -v ^d | head -2 | tail -1 | tr -s " " | cut -f 8 -d " "`
स्पष्टीकरण: