2011-12-15 10 views
5

मैं डेबियन में एक स्क्रिप्ट के साथ एक पेड़ में सबसे पुरानी फ़ाइल को हटाने का प्रयास कर रहा हूं। (? या वहाँ एक आसान/बेहतर तरीका है):xargs और find, rm फाइलनाम में n (newline) के बारे में शिकायत करते हैं

find /home/backups -type f \(-name \*.tgz -o -name \*.gz \) -print0 | xargs -0 ls -t | tail -1 | xargs -0 rm 

लेकिन मैं एक त्रुटि हो रही है

rm: cannot remove `/home/backups/tree/structure/file.2011-12-08_03-01-01.sql.gz\n': No such file or directory 

कोई भी विचार मैं गलत क्या कर रहा हूँ, मैं RTFM की कोशिश की है, लेकिन हार गए कर रहा हूँ।

+1

क्या होता है अगर आप को छोड़ देते हैं '-0' पिछले' xargs' में? – sjngm

उत्तर

11

ls इसका इस्तेमाल एक नई पंक्ति और पिछले xargs संलग्न कर देता है -0 कहता है कि नई लाइन फ़ाइल नाम का हिस्सा है। -0 के बजाय -d '\n' के साथ अंतिम xargs चलाएं।

बीटीडब्लू, जिस तरह से xargs काम करता है, आपकी पूरी पाइप एक बग होने का इंतजार कर रही है। find द्वारा उत्पादित एक बहुत लंबी फ़ाइल नाम सूची पर विचार करें, ताकि xargs -0 ls फ़ाइल नामों के सबसेट के साथ ls कई बार चलाता है। केवल का सबसे पुराना सबसे पुराना ls आमंत्रण इसे tail -1 से पहले बना देगा। यदि सबसे पुरानी फ़ाइल वास्तव में है, तो कहें, find द्वारा पहला फ़ाइल नाम आउटपुट, आप एक छोटी फ़ाइल हटा रहे हैं।

+1

यह पर्याप्त नहीं है। '-d' \ n'' के बिना, 'xargs' प्रत्येक सफेद जगह पर विभाजित होता है, जो शायद आप जो चाहते हैं वह नहीं है। – glglgl

+0

सही, धन्यवाद। वैसे, क्या "वास्तव में लंबी फ़ाइल नाम सूची" माना जाता है? – user1076412

+0

@ user1076412: इस मामले में "लंबा" का अर्थ है "ARG_MAX से बड़ा", जो आपका ओएस सेट होगा। यदि 'find' द्वारा लौटाए गए सभी फ़ाइल नाम ARG_MAX से अधिक लंबे हैं तो xargs इसे एक से अधिक बार आदेश देंगे। – Sorpigal

1

ls विभाजक के रूप में नई लाइनों को उत्सर्जित करता है, इसलिए आपको के साथ दूसरे xargs -0 को प्रतिस्थापित करने की आवश्यकता है। तोड़ता है, हालांकि, अगर सबसे पुरानी फ़ाइल के नाम पर एक नई लाइन है।

0

संपादित करें मुझे ls -t के बिंदु से चूक गया।

क्या मैं इसे बहुत आसान करने का सुझाव दे सकता हूं, उदा।

find /home/backups \ 
    -type f -iregex '.*\.t?gz$' \ 
    -mtime +60 -exec rm {} \; 

जो किसी भी मिलान फ़ाइल एक विशेष आयु से अधिक उम्र (60 दिन, उदाहरण में)


आप tail इस्तेमाल किया, लेकिन यह नहीं बताया है कि null- देखने के लिए नष्ट करेगा सीमांकक।

भले ही, यहाँ एक util है कि आप पिछले 0-सीमांकित तत्व वापस जाने के लिए इस्तेमाल कर सकते हैं है:

#include <string> 
#include <iostream> 
#include <cstdio> 

int main(int argc, const char *argv[]) 
{ 
    std::cin.unsetf(std::ios::skipws); 
    if (! (freopen(NULL, "wb", stdout) && freopen(NULL, "rb", stdin))) 
    { 
     perror("Cannot open stdout/in in binary mode"); 
     return 255; 
    } 

    std::string previous, element; 
    while (std::getline(std::cin, element, '\0')) 
    { 
     previous = element; 
     // if you have c++0x support, use this _instead_ for performance: 
     previous = std::move(element); 
    } 

    std::cout << previous << '\0' << std::flush; 
} 

रूप

find /home/backups -type f \(-name \*.tgz -o -name \*.gz \) -print0 | ./mytail | xargs -0 rm 
+0

याद रखें कि पहला 'xargs' नल-डिलीमीटर और' एलएस 'न्यूलाइन के साथ सीमित करता है, इसलिए पूंछ ठीक है। – thiton

+0

अच्छा विचार है, लेकिन इस प्रश्न के लिए एक प्रकार-दर-समय (ओपी से 'ls -t') गायब है। – thiton

+0

@thiton: इसे ठीक किया गया – sehe

0

ls -tr $(find /home/backups -name '*.gz' -o -name '*.tgz')|head -1|xargs rm -f

+1

यह किसी भी सफेद जगह पर और बहुत लंबी फाइललिस्ट पर टूटता है। – thiton

2

तुम भी बाहर संशोधन समय मुद्रित करने के लिए, प्रकार, होगा पर कट और xargs को खोजने का उपयोग कर सकते हैं:

find /home/backups -printf "%[email protected] %p\n" | sort -n | head -1 | cut -d" " -f2- | xargs ls -al 
+0

यह सबसे अच्छा तरीका है, लेकिन मैं निश्चित ऑफसेट पर भरोसा करने के बजाय एक डेलीमीटर पर कटौती करूंगा। – Sorpigal

+0

आप मेरे ठंडे, मृत हाथों से मेरी "svn status | cut -c8-" का पुरस्कार दे सकते हैं! लेकिन हां एक डिलीमीटर बेहतर होगा – Ken

+0

आपके संपादन ने एक नई बग पेश की है: फ़ाइल नाम में रिक्त स्थान होने पर इसे '-f2-'कहना चाहिए। 'Ls' के बिना सही समाधान के लिए – Sorpigal

3

ls से जुड़े किसी भी समाधान पूरी तरह से गलत है।

यह करने के लिए सही तरीका find उपयोग करने के लिए, फ़ाइलों का सेट लाने के लिए sort, उन्हें कालक्रम के अनुसार आदेश बाहर नष्ट करने के लिए rm लेकिन पहले सभी को फ़िल्टर, तो है। @ केन ने यह अधिकतर सही था, केवल कुछ विवरण गायब थे।

find /home/backups -type f \(-name \*.tgz -o -name \*.gz \) -printf '%[email protected] %p\0' |\ 
    sort -z -n | \ 
    { IFS= read -d '' file ; [ -n "$file" ] && echo rm -f "$(cut -d' ' -f2- <<<"$file")" ; } 

वास्तव में हटाने के लिए echo हटाएं।

उपर्युक्त कोड उन फ़ाइलों के लिए भी काम करता है जिनमें फ़ाइल नामों में रिक्त स्थान, न्यूलाइन या अन्य असामान्य मान हैं। कोई परिणाम नहीं होने पर यह कुछ भी हानिकारक नहीं होगा।

आप फ़ाइल नाम में नई-पंक्तियों पर तोड़ने के बारे में परवाह नहीं है, तो यह थोड़ा आसान

find /home/backups -type f \(-name \*.tgz -o -name \*.gz \) -printf '%[email protected] %p\n' |\ 
    sort -n |\ 
    head -n 1 |\ 
    cut -d' ' -f2- |\ 
    xargs echo rm 

हो जाता है अंतर यह है कि हम head पर भरोसा कर सकते हैं और इसके बजाय पागल कुछ भी करने की एक पाइप पर cut का उपयोग कर सकते है।

+1

+1। – l0b0

संबंधित मुद्दे