कैसे एक निर्देशिका में नवीनतम फ़ाइल `पूंछ` करने के लिए


20

शेल में, मैं tailनिर्देशिका में नवीनतम फ़ाइल कैसे बना सकता हूं ?


1
क्लोजर पर आते हैं, प्रोग्रामर को पूंछने की जरूरत है!
अमित

पास केवल सुपरयुसर या सर्वरफॉल्ट पर जाने के लिए है। सवाल वहाँ रहेगा, और अधिक लोग हैं कि रुचि हो सकती है यह मिल जाएगा।
Mnementh

यहां वास्तविक समस्या निर्देशिका में सबसे हाल ही में अपडेट की गई फ़ाइल है और मुझे विश्वास है कि इसका जवाब पहले ही दिया जा चुका है (या तो यहां या सुपर यूजर पर, मुझे याद नहीं है)।
dmckee

जवाबों:


24

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कि कोई और विकल्प निर्दिष्ट नहीं किया जा रहा है और निम्नलिखित सब कुछ एक फ़ाइल नाम है, जो फ़ाइल नामों को संभालना सुरक्षित बनाता है जो इसके साथ शुरू होते हैं -


1
@ आकाश: क्योंकि आपको "आश्चर्यजनक" परिणाम मिल सकते हैं, उदाहरण के लिए यदि किसी फ़ाइल में उसके नाम में "असामान्य" अक्षर हैं (लगभग सभी वर्ण कानूनी हैं)।
जॉन ज़्वॉन्क

6
जो लोग अपने फ़ाइल नामों में विशेष वर्णों का उपयोग करते हैं, उन्हें सब कुछ मिलता है :-)

6
Paxdiablo को देखकर टिप्पणी काफी दर्दनाक थी, लेकिन फिर दो लोगों ने इसे वोट दिया! जो लोग छोटी गाड़ी सॉफ्टवेयर लिखते हैं वे जानबूझकर सब कुछ प्राप्त करते हैं।
जॉन Zwinck

4
तो ऊपर दिए गए समाधान में खोजने के लिए -frf विकल्प की कमी के कारण ओएक्सएक्स पर काम नहीं करता है, लेकिन स्टेट कमांड में अंतर के कारण केवल ओएक्सएक्स पर निम्नलिखित काम करता है ... शायद यह अभी भी किसी को मदद करेगाtail -f $(find . -type f -exec stat -f "%m {}" {} \;| sort -n | tail -n 1 | cut -d ' ' -f 2)
ऑडियो

2
"दुर्भाग्य से जीएनयू भी है headऔर tailस्विच को स्वीकार नहीं करता है ताकि उन्हें शून्य-सीमांकित इनपुट पर संचालित किया जा सके।" के लिए मेरे प्रतिस्थापन head: … | grep -zm <number> ""
कामिल मैकियोरोस्की

22
tail `ls -t | head -1`

यदि आप रिक्त स्थान वाले फ़ाइलनाम के बारे में चिंतित हैं,

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

1
लेकिन क्या होता है जब आपकी नवीनतम फ़ाइल में स्थान या विशेष वर्ण होते हैं? `` के बजाय $ () का उपयोग करें और इस समस्या से बचने के लिए अपने उपधारा को उद्धृत करें।
फोगग

यह मुझे पंसद है। साफ और सरल। जैसा कि इसे होना चाहिए।

6
यदि आप मजबूत और सही त्याग करते हैं तो साफ और सरल होना आसान है।
फॉग्ज

2
खैर, यह इस बात पर निर्भर करता है कि आप वास्तव में क्या कर रहे हैं। एक समाधान जो हमेशा हर जगह काम करता है, सभी संभव फ़ाइलनामों के लिए, बहुत अच्छा है, लेकिन एक विवश स्थिति में (लॉग फ़ाइलें, उदाहरण के लिए, ज्ञात गैर-अजीब नामों के साथ) यह अनावश्यक हो सकता है।

यह अब तक का सबसे साफ समाधान है। धन्यवाद!
डेमिक्स

4

आप उपयोग कर सकते हैं:

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

$()निर्माण एक उप खोल जो आदेश चलाता है शुरू होता है ls -1tकि के माध्यम से और पाइपिंग (समय के क्रम में सभी फाइलों, प्रति पंक्ति एक लिस्टिंग) head -1पहली पंक्ति (फाइल) प्राप्त करने के लिए।

उस आदेश (सबसे हाल की फ़ाइल) का आउटपुट तब tailसंसाधित होने के लिए पारित किया जाता है।

ध्यान रखें कि यह एक निर्देशिका प्राप्त करने के जोखिम को चलाता है यदि वह सबसे हालिया निर्देशिका प्रविष्टि बनाई गई हो। मैंने उस ट्रिक का उपयोग एक उपनाम में सबसे हाल की लॉग फ़ाइल (एक घूर्णन सेट से) को एक निर्देशिका में संपादित करने के लिए किया है जिसमें केवल उन लॉग फ़ाइलों को शामिल किया गया है।


यह -1आवश्यक नहीं है, lsयह आपके लिए है जब यह एक पाइप में है। उदाहरण के लिए तुलना करें lsऔर ls|cat
अगली सूचना तक रोक दिया गया।

यह लिनक्स के तहत मामला हो सकता है। "सही" यूनिक्स में, प्रक्रियाओं ने अपना व्यवहार नहीं बदला, जहां उनका आउटपुट चल रहा था। यह पाइपलाइन डिबगिंग को वास्तव में कष्टप्रद बना देगा :-)

हम्म, निश्चित नहीं है कि यह सही है - ISTR को एक फ़िल्टर के माध्यम से आउटपुट पाइप करते समय 4.2BSD के तहत कॉलम-स्वरूपित आउटपुट प्राप्त करने के लिए "ls -C" जारी करना होगा, और मुझे पूरा यकीन है कि Solaris के तहत ls उसी तरह से काम करता है। वैसे भी "एक, सच्चा यूनिक्स" क्या है?

उल्लेख। उद्धरण! उल्लेख। उद्धरण! फ़ाइलनामों में रिक्त स्थान हैं!
नॉर्मन रैमसे

@ टीएमएन: एक सही यूनिक्स तरीका गैर-मानव उपभोक्ताओं के लिए एलएस पर निर्भर नहीं है। "यदि आउटपुट टर्मिनल पर है, तो प्रारूप कार्यान्वयन-परिभाषित है।" - यह युक्ति है। यदि आप सुनिश्चित करना चाहते हैं कि आपको ls -1 या ls -C कहना है।
फॉग

4

POSIX सिस्टम पर, "अंतिम निर्मित" निर्देशिका प्रविष्टि प्राप्त करने का कोई तरीका नहीं है। प्रत्येक निर्देशिका प्रविष्टि में Microsoft Windows के विपरीत atime, mtimeऔर है ctime, लेकिन ctimeइसका मतलब क्रिएशनटाइम नहीं है, लेकिन "अंतिम स्थिति परिवर्तन का समय" है।

तो सबसे अच्छा आप प्राप्त कर सकते हैं "पिछले हाल ही में संशोधित फ़ाइल को पूंछें", जिसे अन्य उत्तरों में समझाया गया है। मैं इस कमांड के लिए जाऊंगा:

पूंछ -f "$ (ls -tr | sed 1q)"

lsकमांड के चारों ओर उद्धरण नोट करें । यह स्निपेट लगभग सभी फ़ाइलनामों के साथ काम करता है।


अच्छा काम। सीधा मुद्दे पर। +1
नॉर्मन रैमसे

4

मैं आपको केवल फ़ाइल आकार परिवर्तन देखना चाहता हूं जिसे आप घड़ी का उपयोग कर सकते हैं।

watch -d ls -l

3

इन 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


1

ऐसा करने के लिए शायद एक लाख तरीके हैं, लेकिन जिस तरह से मैं यह करूंगा वह यह है:

tail `ls -t | head -n 1`

बैकटिक्स (पात्रों की तरह उद्धरण) के बीच की बिट्स की व्याख्या की जाती है और परिणाम पूंछ पर लौट आता है।

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

2
बैकटिक्स बुराई हैं। इसके बजाय $ () का उपयोग करें।
विलियम पर्ससेल

1

एक सरल:

tail -f /path/to/directory/*

मेरे लिए ठीक काम करता है।

समस्या उन फ़ाइलों को प्राप्त करने की है जो आपके द्वारा टेल कमांड शुरू करने के बाद उत्पन्न होती हैं। लेकिन अगर आपको इसकी आवश्यकता नहीं है (जैसा कि ऊपर दिए गए सभी समाधान इसकी परवाह नहीं करते हैं), तारांकन सिर्फ सरल समाधान है, IMO।



0

किसी ने इसे पोस्ट किया, और फिर किसी कारण से इसे मिटा दिया, लेकिन यह केवल एक ही है जो काम करता है, इसलिए ...

tail -f `ls -tr | tail`

आप निर्देशिकाओं को बाहर करने के लिए मिला है, यह नहीं है?
amit

1
मैंने इसे मूल रूप से पोस्ट किया था, लेकिन मैंने इसे सोरपीगल से सहमत होने के बाद डिलीट कर दिया था, जिसमें से पार्सिंग आउटपुट lsसबसे स्मार्ट काम नहीं है ...
क्रिस्टोफीडी

मुझे यह जल्दी और गंदा चाहिए, इसमें कोई निर्देशिका नहीं है। इसलिए, यदि आप अपना उत्तर जोड़ेंगे, तो मैं स्वीकार करूंगा कि एक
इत्ते मोव -मालिमोवका

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

स्पष्टीकरण:

  • ls -lt: सभी फाइलों और निर्देशिकाओं की सूची में संशोधन के समय की छंटनी की जाती है
  • grep -v ^ d: निर्देशिकाओं को बाहर करें
  • सिर -2 आगे: आवश्यक फ़ाइल नाम को पार्स करना

1
+1 चतुर के लिए, -2 पार्सिंग ls आउटपुट के लिए, -1 सबस्क्रिप्शन उद्धृत नहीं करने के लिए, -1 एक जादू "फ़ील्ड 8" धारणा के लिए (यह पोर्टेबल नहीं है!) और अंत में -1 भी चालाक के लिए । कुल मिलाकर स्कोर: -4
फोगग

@ बोरिपाल सहमत। हालांकि बुरा उदाहरण मुबारक हो।
अमित

हाँ सोच भी नहीं था कि यह तो कई मामलों में गलत होगा
अमित

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.