निर्देशिका से सभी फ़ाइलों की अंतिम पंक्ति कैसे निकालें?


17

मेरे पास एक निर्देशिका में कई पाठ फाइलें हैं, और मैं निर्देशिका में हर फाइल की अंतिम पंक्ति को हटाना चाहता हूं।

मैं यह कैसे कर सकता हूं?


6
आपने क्या प्रयास किया है? unix.stackexchange.com/help/how-to-ask : "अपने शोध को साझा करना हर किसी की मदद करता है। हमें बताएं कि आपने क्या पाया और यह आपकी आवश्यकताओं को पूरा क्यों नहीं किया। यह दर्शाता है कि आपने खुद की मदद करने के लिए समय निकाल लिया है। , यह हमें स्पष्ट उत्तरों को दोहराने से बचाता है, और सबसे ऊपर, यह आपको अधिक विशिष्ट और प्रासंगिक उत्तर प्राप्त करने में मदद करता है! "
— पैट्रिक

किसी व्यक्ति के गलती से / / पर
— क्रैग

जवाबों:


4

यदि आपके पास पहुंच है vim, तो आप उपयोग कर सकते हैं:

for file in ./*
do
  if [ -f "${file}" ]
  then
    vim -c '$d' -c "wq" "${file}"
  fi
done

16

यदि आप GNU है तो आप इस अच्छे oneliner का उपयोग कर सकते हैं sed।

 sed -i '$ d' ./*

यह वर्तमान निर्देशिका में प्रत्येक गैर-छिपी फ़ाइल की अंतिम पंक्ति को हटा देगा। -iजीएनयू के लिए स्विच का sedमतलब है जगह में काम करना और अंतिम पंक्ति ( मतलब अंतिम और अर्थ हटाना) को हटाने की '$ d'आज्ञा देना ।sed$d


3
यदि फ़ोल्डर में एक नियमित फ़ाइल के अलावा कुछ भी शामिल है, तो यह त्रुटि (और कुछ भी नहीं) को फेंक देगा, एक और फ़ोल्डर ...
— नजीब इद्रिसि

1
@StefanR आप -iवह प्रयोग कर रहे हैं जो एक GNUism है, इसलिए यह लूट है, लेकिन मैं अपनी पुरानी दाढ़ी को खड़ा कर दूंगा अगर मैं यह बताने में विफल रहा कि कुछ पुराने संस्करण sedआपको $और d(या) के बीच कोई व्हाट्सएप नहीं डालने देंगे । सामान्य तौर पर, पैटर्न और कमांड के बीच)।
— zwol

1
@zwol जैसा कि मैंने लिखा है, यह एक त्रुटि के रूप में परिणाम देगा , न कि चेतावनी और sed एक बार उस फ़ाइल तक पहुंचने पर छोड़ देगा (कम से कम मेरे पास sed के संस्करण के साथ)। अगली फ़ाइलों को संसाधित नहीं किया जाएगा। त्रुटि संदेशों को फेंकना एक भयानक विचार होगा क्योंकि आपको पता भी नहीं होगा कि ऐसा हुआ था! Zsh के साथ आप *(.)नियमित फ़ाइलों के लिए ग्लोब का उपयोग कर सकते हैं , मुझे अन्य गोले के बारे में पता नहीं है।
— नजीब इद्रिसि

@NajibIdrissi हम्म, तुम सही हो। यह मुझे आश्चर्यचकित करता है; मुझे उम्मीद थी कि यह निर्देशिका के बारे में शिकायत करेगा, लेकिन फिर कमांड लाइन पर अगली फ़ाइल पर जाएं। वास्तव में, मुझे लगता है कि मैं एक बग के रूप में रिपोर्ट करने जा रहा हूं।
— zwol

@don_crissti मेरे पास GNU sed v4.3 भी है ... मुझे नहीं पता कि आपको क्या बताना है, मैंने अभी फिर से परीक्षण किया। gist.github.com/nidrissi/66fad6be334234f5dbb41c539d84d61e
— नजीब

11

अन्य जवाबों में सभी समस्याएँ हैं यदि निर्देशिका में एक नियमित फ़ाइल के अलावा कुछ है, या फ़ाइल नाम में रिक्त स्थान / newlines के साथ एक फ़ाइल है। यहाँ कुछ है जो परवाह किए बिना काम करता है:

find "$dir" -type f -exec sed -i '$d' '{}' '+'
  • find "$dir" -type f: निर्देशिका में फ़ाइलों को खोजें $dir
    • -type f जो नियमित फाइलें हैं;
    • -exec प्रत्येक फ़ाइल पर कमांड निष्पादित करें
    • sed -i: फ़ाइलों को जगह में संपादित करें;
    • '$d': dअंतिम ( $) लाइन को हटाएं ( ) ।
    • '+': बताता है कि तर्क जोड़ना जारी रखें sed(प्रत्येक फ़ाइल के लिए कमांड चलाने की तुलना में थोड़ा अधिक कुशल, @zwol के लिए धन्यवाद)।

आप उपनिर्देशिकाएं में उतर नहीं करना चाहते हैं, तो आप तर्क जोड़ सकते हैं -maxdepth 1करने के लिए find।


1
हम्म, लेकिन अन्य उत्तरों के विपरीत यह उपनिर्देशिका में उतरता है। (इसके अलावा, इसके वर्तमान संस्करण findअधिक कुशलता से लिखे गए हैं find $dir -type f -exec sed -i '$d' '{}' '+'।)
— zwol

@zwol धन्यवाद, मैंने उस उत्तर में जोड़ दिया है।
— नजीब इदरीसी

-print0पूर्ण आदेश में मौजूद नहीं है, इसे स्पष्टीकरण में क्यों रखा गया है?
— रुस्लान

1
इसके अलावा, -depth 0काम नहीं करता है (खोज 4.4.2), यह इसके बजाय होना चाहिए -maxdepth 1।
— रुस्लान

@Ruslan मेरा पहला संस्करण था जहां मैंने xargs का उपयोग किया था लेकिन फिर याद किया गया -exec।
— नजीब इदरीसी

9

जीएनयू sed -i '$d'का उपयोग करने का मतलब है कि पूरी फ़ाइल को पढ़ना और अंतिम पंक्ति के बिना उसकी एक प्रतिलिपि बनाना, जबकि यह फ़ाइल को जगह में कम करने के लिए (कम से कम बड़ी फ़ाइलों के लिए) बहुत अधिक कुशल होगा।

GNU के साथ truncate, आप यह कर सकते हैं:

for file in ./*; do
  [ -f "$file" ] &&
    length=$(tail -n 1 "$file" | wc -c) &&
    [ "$length" -gt 0 ] &&
    truncate -s "-$length" "$file"
done

यदि फाइलें अपेक्षाकृत छोटी हैं, तो यह संभवतः कम कुशल होगी क्योंकि यह प्रति फ़ाइल कई कमांड चलाती है।

ध्यान दें कि अंतिम नई लाइन वर्ण (अंतिम पंक्ति के बाद) के बाद या दूसरे शब्दों में यदि वे गैर-सीमांकित अंतिम पंक्ति के बाद अतिरिक्त बाइट्स वाली फ़ाइलों के लिए हैं , तो tailकार्यान्वयन के आधार पर , tail -n 1केवल उन अतिरिक्त बाइट्स (जैसे GNU tail) को वापस कर देंगे , या अंतिम (ठीक से सीमांकित) लाइन और उन अतिरिक्त बाइट्स।


आप एक की आवश्यकता है |wc -cमें tailकॉल? (या a ${#length})
— जेफ स्कॉलर a

@JeffSchaller। उफ़। wc -c का आशय वास्तव में था। ${#length}यह काम नहीं करेगा क्योंकि यह वर्णों को गिनता है, बाइट्स नहीं करता है और $(...)टेलिंग न्यूलाइन वर्ण को हटा देगा, ${#...}भले ही सभी वर्ण एकल-बाइट हों।
— स्टीफन चेज़लस 20

6

एक अधिक पोर्टेबल दृष्टिकोण:

for f in ./*
do
test -f "$f" && ed -s "$f" <<\IN
d
w
q
IN
done

मुझे नहीं लगता कि इसके लिए किसी स्पष्टीकरण की आवश्यकता है ... सिवाय इसके कि शायद इस मामले dमें भी यही है $dक्योंकि edडिफ़ॉल्ट रूप से अंतिम पंक्ति का चयन होता है।
यह पुनरावर्ती खोज नहीं करेगा और छिपी हुई फ़ाइलों (उर्फ डॉटफ़ाइल्स) को संसाधित नहीं करेगा।
यदि आप उन लोगों को भी संपादित करना चाहते हैं, तो एक निर्देशिका के अंदर छिपी हुई फ़ाइलों के साथ * मिलान कैसे करें देखें


अच्छा! यदि आप बदलते [[]]हैं []तो यह पूरी तरह से POSIX आज्ञाकारी होगा। ( [[ ... ]]बैशिज़्म है।)
— वाइल्डकार्ड

@Wildcard - धन्यवाद, (हालांकि [[एक बशीवाद नहीं है )
— don_crissti

एक गैर- POSIX-ism, मुझे कहना चाहिए। :)
— वाइल्डकार्ड

3

POSIX- कम्पाइलेंट एक-लाइनर सभी फाइलों के लिए पुनरावर्ती रूप से वर्तमान निर्देशिका में शुरू होता है, जिसमें डॉट-फाइलें भी शामिल हैं:

find . -type f -exec sh -c 'for f; do printf "\$d\nx\n" | ex "$f"; done' sh {} +

.txtकेवल गैर-पुनरावर्ती फ़ाइलों के लिए :

find . -path '*/*/*' -prune -o -type f -name '*.txt' -exec sh -c 'for f; do printf "\$d\nx\n" | ex "$f"; done' sh {} +

और देखें:

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