sed शुरुआती: एक फ़ोल्डर में सभी घटनाओं को बदलना


98

मुझे एक regex खोजने की ज़रूरत है और एक फ़ोल्डर (और उसके सबफ़ोल्डर्स) में सभी फ़ाइलों को प्रतिस्थापित करना है। ऐसा करने के लिए linux shell कमांड क्या होगा?

उदाहरण के लिए, मैं इसे सभी फाइलों पर चलाना चाहता हूं और पुरानी फाइल को नए, बदले हुए पाठ के साथ लिखना चाहता हूं।

sed 's/old text/new text/g' 

जवाबों:


148

केवल sed का उपयोग करके इसे करने का कोई तरीका नहीं है। आपको कम से कम एक साथ उपयोगिता खोजने की आवश्यकता होगी:

find . -type f -exec sed -i.bak "s/foo/bar/g" {} \;

यह कमांड .bakप्रत्येक बदली हुई फाइल के लिए एक फाइल बनाएगी ।

टिप्पणियाँ:

  • -iके लिए तर्क sedआदेश, एक जीएनयू विस्तार है, इसलिए आप इस आदेश को चला रहे हैं बीएसडी के साथ है sedआप कोई नई फ़ाइल के लिए उत्पादन अनुप्रेषित करने की आवश्यकता होगी तो यह नाम बदलें।
  • findउपयोगिता को लागू नहीं करता है -execपुराने यूनिक्स बक्से में तर्क, हां, तो आप एक का उपयोग करने की आवश्यकता होगी | xargsबजाय।

4
किस \;लिए है?
एंड्री मकुखा

4
हमें यह बताने की जरूरत है कि तर्क-वितर्क की आज्ञा कहां से समाप्त होती है? ”। लेकिन शेल एक शेल कमांड विभाजक के रूप में एक ही प्रतीक (;) का उपयोग करता है, इसलिए, हमें "भागने" की आवश्यकता है; शेल से इसे खोजने के लिए -exec तर्क में पास करें।
ऑसेंटाना

2
यह ध्यान देने योग्य है कि -iअपने आप में एक बैकअप फ़ाइल नहीं बनती है, और यही कारण है कि फ़ाइल में जगह पर ऑपरेशन करने के लिए sed होता है।
काइल

1
किस {}लिए है?
somenickname

1
{}प्रत्येक फ़ाइल नाम के द्वारा पाया द्वारा प्रतिस्थापित किया जाएगा findऔर \;लगता है कि आदेश वह इस बिंदु पर खत्म पर अमल करने की जरूरत है कि बताता है।
ओसंताना

51

मैं का उपयोग करना पसंद find | xargs cmdसे अधिकfind -exec है क्योंकि यह याद करने के लिए आसान है।

यह उदाहरण विश्व स्तर पर "foo" को "बार" के साथ .txt फ़ाइलों में आपकी वर्तमान निर्देशिका में या नीचे देता है:

find . -type f -name "*.txt" -print0 | xargs -0 sed -i "s/foo/bar/g"

-print0और -0विकल्पों के बाहर छोड़ दिया जा सकता है अगर आपके फ़ाइलनामों रिक्त स्थान जैसे अजीब वर्ण नहीं है।


3
यदि आप OSX पर हैं, तो प्रयास करें find . -type f -name "*.txt" -print0 | xargs -0 sed -i '' "s/foo/bar/g"(नोट -iतर्क के लिए एक रिक्त स्ट्रिंग प्रदान करें )।
जैकब कुकुल

6

पोर्टेबिलिटी के लिए, मैं उन फीचर्स पर भरोसा नहीं करता, जो कि लिनक्स या बीएसडी के लिए विशिष्ट हैं। इसके बजाय मैं overwriteयूनिक्स प्रोग्रामिंग पर्यावरण पर कर्निघन और पाइक की पुस्तक की स्क्रिप्ट का उपयोग करता हूं ।

आज्ञा तो है

find /the/folder -type f -exec overwrite '{}' sed 's/old/new/g' {} ';'

और overwriteस्क्रिप्ट (जिसका उपयोग मैं सभी जगह करता हूं) है

#!/bin/sh
# overwrite:  copy standard input to output after EOF
# (final version)

# set -x

case $# in
0|1)        echo 'Usage: overwrite file cmd [args]' 1>&2; exit 2
esac

file=$1; shift
new=/tmp/$$.new; old=/tmp/$$.old
trap 'rm -f $new; exit 1' 1 2 15    # clean up files

if "$@" >$new               # collect input
then
    cp $file $old   # save original file
    trap 'trap "" 1 2 15; cp $old $file     # ignore signals
          rm -f $new $old; exit 1' 1 2 15   # during restore
    cp $new $file
else
    echo "overwrite: $1 failed, $file unchanged" 1>&2
    exit 1
fi
rm -f $new $old

विचार यह है कि यह केवल एक फ़ाइल को अधिलेखित करता है यदि कोई कमांड सफल होता है। में उपयोगी findऔर भी जहाँ आप उपयोग नहीं करना चाहेंगे

sed 's/old/new/g' file > file  # THIS CODE DOES NOT WORK

क्योंकि शेल फ़ाइल sedको पढ़ने से पहले ही काट देता है।


3

हो सकता है कि मैं सुझाव दूं (आपकी फ़ाइलों का बैकअप लेने के बाद):

find /the/folder -type f -exec sed -ibak 's/old/new/g' {} ';'

0

उदाहरण: / app / config / फ़ोल्डर और उसके बच्चे के फ़ोल्डर्स के तहत सभी ini फ़ाइलों के लिए 1 के साथ {AutoStart} को फिर से भरें:

sed 's/{AutoStart}/1/g' /app/config/**/*.ini

0
for i in $(ls);do sed -i 's/old_text/new_text/g' $i;done 

5
कृपया अपना उत्तर स्पष्ट करें।
ड्रॉपआउट

हालांकि यह कोड ओपी के मुद्दे को हल कर सकता है, लेकिन यह स्पष्टीकरण देना बेहतर है कि आपका कोड ओपी के मुद्दे को कैसे संबोधित करता है। इस तरह, भविष्य के आगंतुक आपके पोस्ट से सीख सकते हैं, और इसे अपने स्वयं के कोड पर लागू कर सकते हैं। SO एक कोडिंग सेवा नहीं है, बल्कि ज्ञान के लिए एक संसाधन है। उच्च गुणवत्ता, पूर्ण उत्तर इस विचार को पुष्ट करते हैं, और इसके उत्थान की संभावना अधिक होती है। ये विशेषताएं, साथ ही आवश्यकता है कि सभी पोस्ट स्व-निहित हों, एसओ की कुछ ताकतें एक मंच के रूप में हैं जो हमें मंचों से अलग करती हैं। आप अतिरिक्त जानकारी जोड़ने और / या स्रोत प्रलेखन के साथ अपने स्पष्टीकरण के पूरक के लिए संपादित कर सकते हैं।
शेरलहोमन

1
यदि आप इसे नहीं पढ़ सकते हैं, तो बस मेरे उत्तर को भूल जाओ। यह सिर्फ बैश मूल बातें है।
दिमकडक

-1

मेरे सामूहिक खोज / पर्ल स्क्रिप्ट को बदलने का प्रयास करना चाह सकते हैं । जंजीर-उपयोगिता समाधान पर कुछ फायदे हैं (जैसे शेल मेटाचैकर व्याख्या के कई स्तरों से निपटने के लिए नहीं)।

#!/usr/bin/perl

use strict;

use Fcntl qw( :DEFAULT :flock :seek );
use File::Spec;
use IO::Handle;

die "Usage: $0 startdir search replace\n"
    unless scalar @ARGV == 3;
my $startdir = shift @ARGV || '.';
my $search = shift @ARGV or
    die "Search parameter cannot be empty.\n";
my $replace = shift @ARGV;
$search = qr/\Q$search\E/o;

my @stack;

sub process_file($) {
    my $file = shift;
    my $fh = new IO::Handle;
    sysopen $fh, $file, O_RDONLY or
        die "Cannot read $file: $!\n";
    my $found;
    while(my $line = <$fh>) {
        if($line =~ /$search/) {
            $found = 1;
            last;
        }
    }
    if($found) {
        print "  Processing in $file\n";
        seek $fh, 0, SEEK_SET;
        my @file = <$fh>;
        foreach my $line (@file) {
            $line =~ s/$search/$replace/g;
        }
        close $fh;
        sysopen $fh, $file, O_WRONLY | O_TRUNC or
            die "Cannot write $file: $!\n";
        print $fh @file;
    }
    close $fh;
}

sub process_dir($) {
    my $dir = shift;
    my $dh = new IO::Handle;
    print "Entering $dir\n";
    opendir $dh, $dir or
        die "Cannot open $dir: $!\n";
    while(defined(my $cont = readdir($dh))) {
        next
            if $cont eq '.' || $cont eq '..';
        # Skip .swap files
        next
            if $cont =~ /^\.swap\./o;
        my $fullpath = File::Spec->catfile($dir, $cont);
        if($cont =~ /$search/) {
            my $newcont = $cont;
            $newcont =~ s/$search/$replace/g;
            print "  Renaming $cont to $newcont\n";
            rename $fullpath, File::Spec->catfile($dir, $newcont);
            $cont = $newcont;
            $fullpath = File::Spec->catfile($dir, $cont);
        }
        if(-l $fullpath) {
            my $link = readlink($fullpath);
            if($link =~ /$search/) {
                my $newlink = $link;
                $newlink =~ s/$search/$replace/g;
                print "  Relinking $cont from $link to $newlink\n";
                unlink $fullpath;
                my $res = symlink($newlink, $fullpath);
                warn "Symlink of $newlink to $fullpath failed\n"
                    unless $res;
            }
        }
        next
            unless -r $fullpath && -w $fullpath;
        if(-d $fullpath) {
            push @stack, $fullpath;
        } elsif(-f $fullpath) {
            process_file($fullpath);
        }
    }
    closedir($dh);
}

if(-f $startdir) {
    process_file($startdir);
} elsif(-d $startdir) {
    @stack = ($startdir);
    while(scalar(@stack)) {
        process_dir(shift(@stack));
    }
} else {
    die "$startdir is not a file or directory\n";
}

-3

यदि फ़ोल्डर में फ़ाइलों के नाम में कुछ नियमित नाम हैं (जैसे file1, file2 ...) मैंने चक्र के लिए उपयोग किया है।

for i in {1..10000..100}; do sed 'old\new\g' 'file'$i.xml > 'cfile'$i.xml; done

यह पूछे गए प्रश्न से संबंधित नहीं है। प्रश्न में समान फ़ाइल / फ़ोल्डर नाम पैटर्न के बारे में कुछ भी उल्लेख नहीं किया गया है। कृपया ऐसे उत्तरों से बचें
कुणाल पारेख
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.