शेल में, मैं 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 " "`
स्पष्टीकरण: