क्या किसी फ़ाइल को इन-प्लेस संशोधित करने का कोई तरीका है?


54

मेरे पास काफी बड़ी फ़ाइल (35Gb) है, और मैं इस फ़ाइल को सीटू में फ़िल्टर करना चाहूंगा (अर्थात मेरे पास किसी अन्य फ़ाइल के लिए पर्याप्त डिस्क स्थान नहीं है), विशेष रूप से मैं कुछ पैटर्न को पकड़ना और अनदेखा करना चाहता हूं - क्या कोई तरीका है किसी अन्य फ़ाइल का उपयोग किए बिना ऐसा करें?

मान लीजिए कि मैं foo:उदाहरण के लिए सभी लाइनों को फ़िल्टर करना चाहता हूं ...


3
@Theppang: मुझे लगता है कि वह उसी फ़ाइल पर वापस लिखना चाहता है।
फहीम मीठा

5
"इन सीटू" एक लैटिन वाक्यांश है जिसका अर्थ है "जगह में"। सचमुच, "स्थिति में"।
फहीम मीठा

3
उस मामले में, प्रश्न स्पष्ट होना चाहिए, कुछ ऐसा है जैसे फ़ाइल को जगह में संशोधित करने का कोई तरीका है ?
tshepang

5
@Tshepang, "इन सीटू" अंग्रेजी में इस्तेमाल किया जाने वाला एक बहुत ही सामान्य वाक्यांश है जिसका सटीक वर्णन करने के लिए इसका उपयोग किया जाता है - मुझे लगा कि शीर्षक काफी आत्म व्याख्यात्मक था ... @ गिल्स, मुझे बहुत समझ में आया, अधिक डिस्क स्थान की प्रतीक्षा करना आसान है! ;)
निम

2
@ निम: ठीक है, मुझे लगता है कि इन-प्लेस सीटू की तुलना में अधिक सामान्य है ।
tshepang

जवाबों:


41

सिस्टम कॉल स्तर पर यह संभव होना चाहिए। एक कार्यक्रम आपकी ट्रैनिंग के बिना लिखने के लिए आपकी टारगेट फाइल को खोल सकता है और स्टड से जो भी पढ़ता है उसे लिखना शुरू कर देता है। EOF पढ़ते समय, आउटपुट फ़ाइल को छोटा किया जा सकता है।

चूंकि आप इनपुट से लाइनों को फ़िल्टर कर रहे हैं, आउटपुट फाइल राइट पोजिशन हमेशा रीड पोजीशन से कम होनी चाहिए। इसका मतलब है कि आपको नए आउटपुट के साथ अपने इनपुट को दूषित नहीं करना चाहिए।

हालांकि, ऐसा करने वाला प्रोग्राम ढूंढना ही समस्या है। dd(1)विकल्प है conv=notruncजो खुले पर आउटपुट फ़ाइल को छोटा नहीं करता है, लेकिन यह अंत में भी छोटा नहीं करता है, मूल फ़ाइल सामग्री को grep सामग्री (जैसे कमांड के साथ grep pattern bigfile | dd of=bigfile conv=notrunc) के बाद छोड़ देता है

चूंकि यह सिस्टम कॉल के दृष्टिकोण से बहुत सरल है, इसलिए मैंने एक छोटा प्रोग्राम लिखा और एक छोटे (1MiB) पूर्ण लूप फाइल सिस्टम पर इसका परीक्षण किया। यह वही था जो आप चाहते थे, लेकिन आप वास्तव में पहले कुछ अन्य फाइलों के साथ यह परीक्षण करना चाहते हैं। यह हमेशा एक फ़ाइल को ओवरराइट करने के लिए जोखिम भरा होने वाला है।

overwrite.c

/* This code is placed in the public domain by camh */

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>

int main(int argc, char **argv)
{
        int outfd;
        char buf[1024];
        int nread;
        off_t file_length;

        if (argc != 2) {
                fprintf(stderr, "usage: %s <output_file>\n", argv[0]);
                exit(1);
        }
        if ((outfd = open(argv[1], O_WRONLY)) == -1) {
                perror("Could not open output file");
                exit(2);
        }
        while ((nread = read(0, buf, sizeof(buf))) > 0) {
                if (write(outfd, buf, nread) == -1) {
                        perror("Could not write to output file");
                        exit(4);
                }
        }
        if (nread == -1) {
                perror("Could not read from stdin");
                exit(3);
        }
        if ((file_length = lseek(outfd, 0, SEEK_CUR)) == (off_t)-1) {
                perror("Could not get file position");
                exit(5);
        }
        if (ftruncate(outfd, file_length) == -1) {
                perror("Could not truncate file");
                exit(6);
        }
        close(outfd);
        exit(0);
}

आप इसका उपयोग इस प्रकार करेंगे:

grep pattern bigfile | overwrite bigfile

इससे पहले कि आप इसे आज़माएं, मैं ज्यादातर दूसरों के लिए टिप्पणी करने के लिए इसे पोस्ट कर रहा हूं। शायद किसी और को एक ऐसे कार्यक्रम के बारे में पता है जो कुछ ऐसा ही करता है जो अधिक परीक्षण किया जाता है।


मैं देखना चाहता था कि क्या मैं इसके लिए कुछ लिखे बिना दूर हो सकता हूं! :) मुझे लगता है कि यह चाल चलेगा! धन्यवाद!
निम

2
सी के लिए +1; काम करने लगता है, लेकिन मुझे एक संभावित समस्या दिखाई देती है: फ़ाइल को बाईं ओर से उस समय पढ़ा जा रहा है जब दाईं ओर एक ही फ़ाइल लिख रही है और जब तक आप दो प्रक्रियाओं का समन्वय नहीं करते हैं, तो आप समस्याओं को संभावित रूप से एक ही पर लिख देंगे। ब्लॉक। फ़ाइल अखंडता के लिए छोटे ब्लॉक आकार का उपयोग करना बेहतर हो सकता है क्योंकि अधिकांश मुख्य उपकरण 8192 का उपयोग करेंगे। यह संघर्ष को टालने के लिए कार्यक्रम को धीमा कर सकता है (लेकिन गारंटी नहीं दे सकता है)। हो सकता है कि मेमोरी में बड़े हिस्से पढ़ें (सभी नहीं) और छोटे ब्लॉक में लिखें। एक nanosleep (2) / usleep (3) भी जोड़ सकता है।
Arcege

4
@ प्रतिबंध: लेखन ब्लॉक में नहीं किया जाता है। यदि आपकी पढ़ने की प्रक्रिया में 2 बाइट्स पढ़े गए हैं और आपकी लिखने की प्रक्रिया 1 बाइट लिखती है, तो केवल पहली बाइट बदल जाएगी और पठन प्रक्रिया 3 बाइट्स पर मूल सामग्री के साथ अपरिवर्तित पढ़ना जारी रख सकती है। चूंकि grepयह पढ़ने से अधिक डेटा आउटपुट नहीं करेगा, इसलिए लिखने की स्थिति हमेशा पढ़ने की स्थिति के पीछे होनी चाहिए। यहां तक ​​कि अगर आप पढ़ने के रूप में उसी दर पर लिख रहे हैं, तो भी यह ठीक होगा। इस grep के बजाय, और फिर से फिर से कोशिश करें। पहले और बाद में md5sum और आप इसे देखेंगे।
कैमह

6
अच्छा लगा। यह जॉय हेस की अधिकताओं के लिए एक मूल्यवान अतिरिक्त हो सकता है । आप उपयोग कर सकते हैंdd , लेकिन यह बोझिल है।
गिलेस एसओ- बुराई को रोकना '

'जीआरईपी पैटर्न बिगफाइल | बिगफाइल को अधिलेखित करें '- मुझे यह काम बिना त्रुटियों के मिला है, लेकिन जो मुझे समझ में नहीं आया है - क्या यह किसी अन्य पाठ के साथ पैटर्न में बदलने की आवश्यकता नहीं है? तो ऐसा नहीं होना चाहिए: 'grep पैटर्न bigfile | ओवरराइट / रिप्लेस-टेक्स्ट / बिगफाइल '
अलेक्जेंडर मिल्स

20

आप sedफ़ाइलों को जगह में संपादित करने के लिए उपयोग कर सकते हैं (लेकिन यह एक मध्यवर्ती अस्थायी फ़ाइल बनाता है):

युक्त सभी लाइनों को हटाने के लिए foo:

sed -i '/foo/d' myfile

सभी लाइनों को रखने के लिए foo:

sed -i '/foo/!d' myfile

दिलचस्प, क्या इस अस्थायी फ़ाइल को मूल के समान आकार की आवश्यकता होगी?
निम

3
हां, तो यह शायद अच्छा नहीं है।
pjc50

17
यह वह नहीं है जो ओपी के लिए पूछ रहा है क्योंकि यह एक दूसरी फ़ाइल बनाता है।
Arcege

1
यह समाधान केवल पढ़ने के लिए फाइल सिस्टम, पर असफल हो जायेगी जहां "रीड-ओनली" मतलब है कि आपके $HOME जाएगा लिखने योग्य हो, लेकिन /tmpहो जाएगा -केवल पढ़ने के लिए (डिफ़ॉल्ट रूप से)। उदाहरण के लिए, यदि आपके पास उबंटू है और आपने रिकवरी कंसोल में बूट किया है, तो यह आमतौर पर होता है। इसके अलावा, यहाँ-दस्तावेज़ ऑपरेटर <<<वहां भी काम नहीं करेगा, क्योंकि इसके लिए r / w/tmp होना आवश्यक है क्योंकि यह एक अस्थायी फ़ाइल को वहाँ भी लिखेगा। (cf. यह प्रश्नstrace
inc

हाँ, यह मेरे लिए भी काम नहीं करेगा, मेरे द्वारा की गई सभी सेड कमांड्स वर्तमान फ़ाइल को एक नई फ़ाइल (-इन-प्लेस फ्लैग के बावजूद) के साथ बदल देगी।
अलेक्जेंडर मिल्स

19

मैं मान लूंगा कि आपका फ़िल्टर कमांड वही है जिसे मैं एक उपसर्ग सिकुड़ने वाला फ़िल्टर कहूंगा , जिसकी संपत्ति है कि आउटपुट में बाइट एन इनपुट के कम से कम एन बाइट्स पढ़ने से पहले कभी नहीं लिखा जाता है। grepयह संपत्ति है (जब तक कि यह केवल फ़िल्टरिंग है और मैचों के लिए लाइन नंबर जोड़ने जैसी अन्य चीजें नहीं कर रहा है)। इस तरह के एक फिल्टर के साथ, आप इनपुट को ओवरराइट कर सकते हैं जैसे आप साथ जाते हैं। बेशक, आपको कोई गलती न करने की ज़रूरत है, क्योंकि फ़ाइल की शुरुआत में ओवरराइट किया गया हिस्सा हमेशा के लिए खो जाएगा।

अधिकांश यूनिक्स उपकरण केवल एक फ़ाइल को जोड़ने या इसे ओवरराइट करने की संभावना के साथ, इसे छोटा करने का विकल्प देते हैं। मानक टूलबॉक्स में एक अपवाद है dd, जिसे अपनी आउटपुट फ़ाइल को छोटा नहीं करने के लिए कहा जा सकता है। तो कमांड को फिल्टर करने की योजना है dd conv=notrunc। यह फ़ाइल के आकार को परिवर्तित नहीं करता है, इसलिए हम नई सामग्री की लंबाई को भी पकड़ लेते हैं और फ़ाइल को उस लंबाई (फिर से dd) से काटते हैं । ध्यान दें कि यह कार्य स्वाभाविक रूप से गैर-मजबूत है - यदि कोई त्रुटि होती है, तो आप अपने दम पर हैं।

export LC_ALL=C
n=$({ grep -v foo <big_file |
      tee /dev/fd/3 |
      dd of=big_file conv=notrunc; } 3>&1 | wc -c)
dd if=/dev/null of=big_file bs=1 seek=$n

आप rougly समकक्ष पर्ल लिख सकते हैं। यहां एक त्वरित कार्यान्वयन है जो कुशल होने का प्रयास नहीं करता है। बेशक, आप अपना प्रारंभिक फ़िल्टरिंग सीधे उस भाषा में भी कर सकते हैं।

grep -v foo <big_file | perl -e '
  close STDOUT;
  open STDOUT, "+<", $ARGV[0] or die;
  while (<STDIN>) {print}
  truncate STDOUT, tell STDOUT or die
' big_file

16

किसी भी बॉर्न जैसे शेल के साथ:

{
  cat < bigfile | grep -v to-exclude
  perl -e 'truncate STDOUT, tell STDOUT'
} 1<> bigfile

किसी कारण से, ऐसा लगता है कि लोग उस 40 साल पुराने और मानक रीड + राइट रीडायरेक्शन ऑपरेटर के बारे में भूल जाते हैं ।

हम खोलने bigfileपढ़ने + राइट मोड में और (क्या मायने रखती है सबसे यहाँ) के बिना पर काट-छांट stdout, जबकि bigfileपर खुला (अलग से) है catकी stdingrepसमाप्त हो जाने के बाद , और अगर इसने कुछ पंक्तियों को हटा दिया है, तो stdoutअब कहीं भीतर इंगित करता है bigfile, हमें इस बिंदु से आगे निकलने के लिए छुटकारा पाने की आवश्यकता है। इसलिए वर्तमान स्थिति perlमें फ़ाइल ( truncate STDOUT) को छोटा करने वाली कमांड (जैसा कि लौटाया गया है tell STDOUT)।

(यह catGNU के लिए है grepजो अन्यथा शिकायत करता है अगर स्टड और stdout एक ही फ़ाइल को इंगित करते हैं)।


¹ ठीक है, जबकि <>सत्तर के दशक के उत्तरार्ध में बॉर्न शेल में रहा था, यह शुरू में अनिर्दिष्ट था और इसे ठीक से लागू नहीं किया गया था । यह ash1989 से मूल कार्यान्वयन में नहीं था , और यह एक POSIX shपुनर्निर्देशन ऑपरेटर है (क्योंकि 90 के दशक की शुरुआत में POSIX shपर आधारित था, ksh88जिस पर हमेशा यह होता था), इसे sh2000 तक उदाहरण के लिए FreeBSD में नहीं जोड़ा गया था , इसलिए 15 साल तक पुराना शायद अधिक सटीक है। यह भी ध्यान दें कि जब निर्दिष्ट नहीं किया गया है तो डिफ़ॉल्ट फ़ाइल डिस्क्रिप्टर <>सभी गोले में है, सिवाय इसके कि ksh93यह 2010 में ksh93t + में 0 से 1 में बदल गया (पीछे की संगतता और POSIX अनुपालन तोड़कर)


2
क्या आप समझा सकते हैं perl -e 'truncate STDOUT, tell STDOUT'? यह मेरे लिए बिना शामिल किए काम करता है। पर्ल का उपयोग किए बिना एक ही चीज़ को प्राप्त करने का कोई तरीका?
आरोन ब्लेनकुश

1
@AaronBlenkush, देखें संपादित करें।
स्टीफन चेज़लस

1
बिल्कुल शानदार - धन्यवाद। मैं वहाँ था, लेकिन यह याद नहीं है .... "36 वर्षीय" मानक के लिए एक संदर्भ मजेदार होगा, क्योंकि इसका उल्लेख en.wikipedia.org/wiki/Bourne_shell पर नहीं है । और इसके लिए क्या इस्तेमाल किया गया था? मैं SunOS 5.6 में एक बग फिक्स का संदर्भ देखता हूं: redirection "<>" fixed and documented (used in /etc/inittab f.i.). जो एक संकेत है।
nealmcb

2
@nealmcb, देखें संपादित करें।
स्टीफन चेजालस

@ StéphaneChazelas इस उत्तर की तुलना में आपका समाधान कैसे है ? यह स्पष्ट रूप से एक ही काम करता है लेकिन सरल दिखता है।
अखाण

9

भले ही यह एक पुराना सवाल है, लेकिन मुझे लगता है कि यह एक बारहमासी सवाल है, और एक अधिक सामान्य, स्पष्ट समाधान उपलब्ध है जो अब तक सुझाया गया है। क्रेडिट जहां क्रेडिट की वजह से है: मुझे यकीन नहीं है कि मैं स्टीफन चेज़ेलस के <>अद्यतन ऑपरेटर के उल्लेख पर विचार किए बिना इसके साथ आया हूं ।

बॉर्न शेल में अपडेट के लिए एक फाइल खोलना सीमित उपयोगिता का है। शेल आपको किसी फ़ाइल की तलाश करने का कोई रास्ता नहीं देता है, और इसकी नई लंबाई निर्धारित करने का कोई तरीका नहीं है (यदि पुराने की तुलना में छोटा है)। लेकिन यह आसानी से है, इसलिए आसानी से मुझे आश्चर्य है कि यह मानक उपयोगिताओं में नहीं है /usr/bin

यह काम:

$ grep -n foo T
8:foo
$ (exec 4<>T; grep foo T >&4 && ftruncate 4) && nl T; 
     1  foo

जैसा कि यह (स्टीफन से हैट टिप):

$ { grep foo T && ftruncate; } 1<>T  && nl T; 
     1  foo

(मैं GNU grep का उपयोग कर रहा हूँ। शायद कुछ बदल गया है क्योंकि उन्होंने अपना उत्तर लिखा है।)

को छोड़कर, आपके पास कोई / usr / bin / ftruncate नहीं है । C की एक दो दर्जन पंक्तियों के लिए, आप नीचे देख सकते हैं। यह ftruncate उपयोगिता एक मनमाना फ़ाइल डिस्क्रिप्टर को एक मनमाना लंबाई तक पहुंचाती है , जो मानक आउटपुट और वर्तमान स्थिति के लिए डिफ़ॉल्ट है।

उपरोक्त आदेश (पहला उदाहरण)

  • Tअद्यतन के लिए फ़ाइल विवरणक 4 खोलता है । बस के रूप में खुला (2) के साथ, इस तरह से फ़ाइल को खोलने पर वर्तमान ऑफसेट 0 पर स्थित है।
  • grepT सामान्य रूप से प्रक्रिया करता है , और शेल Tडिस्क्रिप्टर 4 के माध्यम से अपने आउटपुट को रीडायरेक्ट करता है ।
  • ftruncate डिस्क्रिप्टर 4 पर ftruncate (2) को कॉल करता है, जो वर्तमान ऑफ़सेट के मान की लंबाई निर्धारित करता है (ठीक उसी जगह जहां grep ने इसे छोड़ा था)।

उपधारा फिर बाहर निकलता है, विवरणक 4 बंद होता है। यहाँ ftruncate है :

#include <err.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int
main( int argc, char *argv[] ) {
  off_t i, fd=1, len=0;
  off_t *addrs[2] = { &fd, &len };

  for( i=0; i < argc-1; i++ ) {
    if( sscanf(argv[i+1], "%lu", addrs[i]) < 1 ) {
      err(EXIT_FAILURE, "could not parse %s as number", argv[i+1]);
    }
  }

  if( argc < 3 && (len = lseek(fd, 0, SEEK_CUR)) == -1 ) {
    err(EXIT_FAILURE, "could not ftell fd %d as number", (int)fd);
  }


  if( 0 != ftruncate((int)fd, len) ) {
    err(EXIT_FAILURE, argc > 1? argv[1] : "stdout");
  }

  return EXIT_SUCCESS;
}

इस तरह से उपयोग किए जाने पर NB, ftruncate (2) नॉनपोर्टेबल होता है। पूर्ण सामान्यता के लिए, अंतिम लिखित बाइट पढ़ें, फ़ाइल O_WronLY को फिर से खोलें, बाइट लिखें, और बंद करें।

यह देखते हुए कि प्रश्न 5 वर्ष पुराना है, मैं यह कहने जा रहा हूं कि यह समाधान अप्रतिष्ठित है। यह एक नया डिस्क्रिप्टर खोलने के लिए निष्पादन का लाभ उठाता है , और <>ऑपरेटर, जो दोनों आर्कन हैं। मैं एक मानक उपयोगिता के बारे में नहीं सोच सकता जो फ़ाइल डिस्क्रिप्टर द्वारा एक इनोड को हेरफेर करता है। (सिंटैक्स हो सकता है ftruncate >&4, लेकिन मुझे यकीन नहीं है कि एक सुधार है।) यह कैम के सक्षम, खोजपूर्ण उत्तर की तुलना में काफी कम है। यह स्टीफ़न, IMO की तुलना में थोड़ा स्पष्ट है, जब तक कि आप पर्ल को मुझसे अधिक पसंद नहीं करते। मुझे आशा है कि किसी को यह उपयोगी लगता है।

एक ही काम करने का एक अलग तरीका लेसेक (2) का एक निष्पादन योग्य संस्करण होगा जो वर्तमान ऑफसेट की रिपोर्ट करता है; आउटपुट का उपयोग / usr / bin / truncate के लिए किया जा सकता है , जो कुछ लिनक्स प्रदान करते हैं।


5

ed संभवत: किसी फ़ाइल को जगह में संपादित करने का सही विकल्प है:

ed my_big_file << END_OF_ED_COMMANDS
g/foo:/d
w
q 
END_OF_ED_COMMANDS

मुझे यह विचार पसंद है, लेकिन जब तक अलग-अलग edसंस्करण अलग-अलग व्यवहार नहीं करते हैं ..... यह man ed(जीएनयू एड 1.4) से है ...If invoked with a file argument, then a copy of file is read into the editor's buffer. Changes are made to this copy and not directly to file itself.
पीटर।

@ यदि आप यह अनुमान लगा रहे हैं कि परिवर्तनों को सहेजना नामांकित फ़ाइल को प्रभावित नहीं करेगा, तो आप गलत हैं। मैं उस उद्धरण की व्याख्या यह कहने के लिए करता हूं कि आपके परिवर्तन तब तक परिलक्षित नहीं होते हैं जब तक आप उन्हें सहेजते हैं। मैं edमानता हूं कि 35 जीबी फ़ाइलों को संपादित करने के लिए एक गॉल समाधान नहीं है क्योंकि फ़ाइल को एक बफर में पढ़ा जाता है।
ग्लेन जैकमैन 19

2
मैं सोच रहा था कि इसका मतलब है कि पूरी फ़ाइल बफर में लोड हो जाएगी .. लेकिन शायद केवल अनुभाग (एस) यह neeeds बफर में लोड कर रहे हैं .. मैं थोड़ी देर के लिए एड के बारे में उत्सुक रहा हूँ ... मुझे लगा कि इन-सीटू संपादन कर सकता है ... मुझे बस एक बड़ी फ़ाइल आज़माना होगा ... यदि यह काम करता है तो यह एक उचित समाधान है, लेकिन जैसा कि मैं लिखता हूं, मैं यह सोचना शुरू कर रहा हूं कि यह क्या प्रेरित सेड हो सकता है ( बड़े डेटा विखंडू के साथ काम करने से मुक्त ... मैंने देखा है कि 'एड' वास्तव में एक स्क्रिप्ट से स्ट्रीम किए गए इनपुट को स्वीकार कर सकता है (इसके साथ उपसर्ग किया गया है !), इसलिए इसके आस्तीन पर कुछ और दिलचस्प ट्रिक्स हो सकते हैं।
पीटर

मुझे पूरा यकीन है कि edफ़ाइल को लिखने की प्रक्रिया को फ़ाइल में काट दिया गया है और इसे फिर से लिखा गया है। तो यह ओपी इच्छाओं के रूप में डिस्क पर जगह में डेटा को बदल नहीं देगा। इसके अलावा, यह काम नहीं कर सकता है यदि फ़ाइल मेमोरी में लोड होने के लिए बहुत बड़ी है।
निक मैट्टो

5

आप अपनी फ़ाइल खोलने के लिए एक बैश रीड / राइट फ़ाइल डिस्क्रिप्टर का उपयोग कर सकते हैं (फिर इसे इन-सीटू को अधिलेखित करने के लिए), फिर sedऔर truncate... लेकिन निश्चित रूप से, अपने परिवर्तनों को अब तक पढ़े गए डेटा की मात्रा से बड़ा न होने दें। ।

यहाँ स्क्रिप्ट है (उपयोग: bash चर $ BashPID)

# Create a test file
  echo "going abc"  >junk
  echo "going def" >>junk
  echo "# ORIGINAL file";cat junk |tee >( wc=($(wc)); echo "# ${wc[0]} lines, ${wc[2]} bytes" ;echo )
#
# Assign file to fd 3, and open it r/w
  exec 3<> junk  
#
# Choose a unique filename to hold the new file size  and the pid 
# of the semi-asynchrounous process to which 'tee' streams the new file..  
  [[ ! -d "/tmp/$USER" ]] && mkdir "/tmp/$USER" 
  f_pid_size="/tmp/$USER/pid_size.$(date '+%N')" # %N is a GNU extension: nanoseconds
  [[ -f "$f_pid_size" ]] && { echo "ERROR: Work file already exists: '$f_pid_size'" ;exit 1 ; }
#
# run 'sed' output to 'tee' ... 
#  to modify the file in-situ, and to count the bytes  
  <junk sed -e "s/going //" |tee >(echo -n "$BASHPID " >"$f_pid_size" ;wc -c >>"$f_pid_size") >&3
#
#@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
# The byte-counting process is not a child-process, 
# so 'wait' doesn't work... but wait we must...  
  pid_size=($(cat "$f_pid_size")) ;pid=${pid_size[0]}  
  # $f_pid_size may initially contain only the pid... 
  # get the size when pid termination is assured
  while [[ "$pid" != "" ]] ; do
    if ! kill -0 "$pid" 2>/dev/null; then
       pid=""  # pid has terminated. get the byte count
       pid_size=($(cat "$f_pid_size")) ;size=${pid_size[1]}
    fi
  done
  rm "$f_pid_size"
#@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
#
  exec 3>&- # close fd 3.
  newsize=$(cat newsize)
  echo "# MODIFIED file (before truncating)";cat junk |tee >( wc=($(wc)); echo "# ${wc[0]} lines, ${wc[2]} bytes" ;echo )  cat junk
#
 truncate -s $newsize junk
 echo "# NEW (truncated) file";cat junk |tee >( wc=($(wc)); echo "# ${wc[0]} lines, ${wc[2]} bytes" ;echo )  cat junk
#
exit

यहाँ परीक्षण आउटपुट है

# ORIGINAL file
going abc
going def
# 2 lines, 20 bytes

# MODIFIED file (before truncating)
abc
def
c
going def
# 4 lines, 20 bytes

# NEW (truncated) file
abc
def
# 2 lines, 8 bytes

3

मैं फ़ाइल को मेमोरी-मैप करूँगा, नंगे मेमोरी में चार * पॉइंटर्स का उपयोग करके इन-प्लेस सब कुछ करूँगा, फिर फ़ाइल को अनमैप कर दूंगा और उसे काट दूंगा।


3
+1, लेकिन केवल इसलिए कि 64-बिट CPU और OSes की व्यापक उपलब्धता 35 GB फ़ाइल के साथ अब ऐसा करना संभव बनाती है। वे अभी भी 32-बिट सिस्टम पर (इस साइट के दर्शकों के विशाल बहुमत, मुझे संदेह है) इस समाधान का उपयोग करने में सक्षम नहीं होंगे।
वारेन यंग

2

इन-सीटू बिल्कुल नहीं लेकिन यह समान परिस्थितियों में उपयोग का हो सकता है।
यदि डिस्क स्थान एक समस्या है, तो पहले फ़ाइल को संपीड़ित करें (क्योंकि यह पाठ है यह एक बड़ी कमी देगा) फिर एक असम्पीडित / संक्षिप्त पाइपलाइन के बीच में सामान्य तरीके से सेड (या grep, या जो भी हो) का उपयोग करें।

# Reduce size from ~35Gb to ~6Gb
$ gzip MyFile

# Edit file, creating another ~6Gb file
$ gzip -dc <MyFile.gz | sed -e '/foo/d' | gzip -c >MyEditedFile.gz

2
लेकिन निश्चित रूप से gzip संपीड़ित संस्करण के साथ बदलने से पहले डिस्क को संपीड़ित संस्करण लिख रहा है, इसलिए आपको अन्य विकल्पों के विपरीत कम से कम इतना अतिरिक्त स्थान चाहिए। लेकिन यह सुरक्षित है, अगर आपको जगह मिल गई है (जो मैं नहीं ....)
nealmcb

यह एक चतुर समाधान है जिसे आगे दो के बजाय केवल एक संपीड़न करने के लिए अनुकूलित किया जा सकता है:sed -e '/foo/d' MyFile | gzip -c >MyEditedFile.gz && gzip -dc MyEditedFile.gz >MyFile
टोड ओवेन

0

इस सवाल का googling किसी के लाभ के लिए, सही जवाब है रोक अस्पष्ट खोल सुविधाओं है कि नगण्य प्रदर्शन लाभ के लिए अपनी फ़ाइल को भ्रष्ट जोखिम की तलाश में है और इसकी जगह इस पैटर्न के कुछ बदलाव का उपयोग करें:

grep "foo" file > file.new && mv file.new file

केवल बेहद असामान्य स्थिति में कि यह किसी कारण से संभव नहीं है, क्या आपको इस पृष्ठ पर किसी भी अन्य उत्तर पर गंभीरता से विचार करना चाहिए (हालांकि वे निश्चित रूप से पढ़ने के लिए दिलचस्प हैं)। मैं स्वीकार करूंगा कि ओपी की दूसरी फाइल बनाने के लिए कोई डिस्क स्थान नहीं होने की आशंका बिल्कुल ऐसी ही स्थिति है। हालाँकि तब भी, वहाँ अन्य विकल्प उपलब्ध हैं, जैसे @Ed Randall और @Basile Starynkevitch द्वारा प्रदान किया गया।


1
मुझे गलतफहमी हो सकती है, लेकिन ओपी ने जो पूछा उसका कोई लेना देना नहीं है। अस्थायी फ़ाइल के लिए पर्याप्त डिस्क के बिना बिगफाइल का उर्फ ​​इनलाइन संपादन।
कीवी

@ कीवी यह इस सवाल के अन्य दर्शकों के उद्देश्य से एक उत्तर है (जिनमें से अब तक लगभग 15,000 हो चुके हैं)। सवाल "क्या किसी फ़ाइल को जगह में संशोधित करने का कोई तरीका है?" ओपी के विशिष्ट उपयोग के मामले की तुलना में व्यापक प्रासंगिकता है।
टोड ओवेन

-3

echo -e "$(grep pattern bigfile)" >bigfile


3
यह काम नहीं करता है यदि फ़ाइल बड़ी है और greppedडेटा कमांडलाइन की अनुमति देने की लंबाई से अधिक है। इसके बाद यह डेटा को
नष्ट कर देता है
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.