sed acemi: bir klasördeki tüm oluşumların değiştirilmesi


98

Bir klasördeki (ve alt klasörlerindeki) tüm dosyalar için bir normal ifade bulup değiştirmem gerekiyor. Bunu yapmak için linux shell komutu ne olabilir?

Örneğin, bunu tüm dosyalar üzerinde çalıştırmak ve eski dosyanın üzerine yeni, değiştirilmiş metni yazmak istiyorum.

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

Yanıtlar:


150

Sadece sed kullanarak yapmanın bir yolu yok. En azından bulma yardımcı programını birlikte kullanmanız gerekecek:

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

Bu komut, .bakdeğiştirilen her dosya için bir dosya oluşturacaktır .

Notlar:

  • -iArgümanı sedBSD var olan bu komutu çalıştırıyorsanız komuta, yani, bir GNU oluşumudur sedsonra yeniden adlandırın yeni bir dosyaya çıktı yönlendirmek gerekecektir.
  • Yardımcı findprogram -execargümanı eski UNIX kutularında uygulamaz, bu nedenle | xargsbunun yerine a kullanmanız gerekir .

4
Ne \;için?
Andriy Makukha

4
-Exec argümanının komutunun a ”;” ile nerede bittiğini bulmamız gerekiyor. Ancak kabuk, kabuk komut ayırıcısı olarak aynı sembolü (;) kullanır, bu yüzden ";" Kabuktan find'ın -exec argümanına iletmek için.
osantana

2
Kendi -ibaşına bir yedekleme dosyası oluşturmadığını ve sed'in dosya üzerinde işlemi yerine getirmesine neden olan şeyin bu olduğunu belirtmek gerekir .
Kyle

1
Ne {}için?
somenickname

1
{}Tarafından bulunan her dosya adına göre değiştirilecektir findve \;bu komut o bu noktada bitirmek yürütmek için ihtiyaç duyduğu bulmak söyler.
osantana

53

Ben kullanmayı tercih find | xargs cmdüzerinde find -exechatırlaması kolay olduğu için.

Bu örnek genel olarak geçerli dizininizdeki veya altındaki .txt dosyalarındaki "foo" ifadesini "bar" ile değiştirir:

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

-print0Ve -0Dosya adlarınız boşluk gibi korkak karakterler içeren yoksa seçenekler dışarı bırakılabilir.


3
OSX kullanıyorsanız, deneyin find . -type f -name "*.txt" -print0 | xargs -0 sed -i '' "s/foo/bar/g"( -iargümana boş bir dize sağlamaya dikkat edin ).
Jakub Kukul

MacOS'ta sed -i.bakbunun yerine çalıştırın sed -i. Sanırım @JakubKukul'un sed -i ''da bahsettiği gibi, işe yarıyor.
forzagreen

7

Taşınabilirlik için, linux veya BSD'ye özgü sed özelliklerine güvenmiyorum. Bunun yerine overwriteKernighan ve Pike'ın Unix Programlama Ortamı ile ilgili kitabındaki senaryoyu kullanıyorum .

Komut o zaman

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

Ve overwritesenaryo (her yerde kullandığım)

#!/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

Buradaki fikir, yalnızca bir komut başarılı olursa bir dosyanın üzerine yazmasıdır. Hem içinde findhem de kullanmak istemediğiniz yerlerde kullanışlıdır

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

çünkü kabuk dosyayı sedokuyamadan keser .


3

Önerebilir miyim (dosyalarınızı yedekledikten sonra):

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

0

Örnek: / app / config / klasörü ve alt klasörleri altındaki tüm ini dosyaları için {AutoStart} 'ı 1 ile replase edin:

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

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

6
Lütfen cevabınızı açıklayınız.
Çıkarma

1
Bu kod, OP'nin sorununu çözebilirken, kodunuzun OP sorununu nasıl ele aldığına dair bir açıklama eklemek daha iyidir. Bu şekilde, gelecekteki ziyaretçiler gönderinizden öğrenebilir ve kendi kodlarına uygulayabilir. SO bir kodlama hizmeti değil, bilgi için bir kaynaktır. Yüksek kaliteli, eksiksiz yanıtlar bu fikri güçlendirir ve daha çok oy almaları muhtemeldir. Bu özellikler ve tüm gönderilerin bağımsız olması gerekliliği, bizi forumlardan ayıran bir platform olarak SO'nun bazı güçlü yönleridir. Ek bilgi eklemek ve / veya açıklamalarınızı kaynak belgelerle desteklemek için düzenleyebilirsiniz.
SherylHohman

2
Bunu okuyamıyorsanız, cevabımı unutun. Sadece bash temelleri.
DimiDak

0

Bu benim için çalıştı (mac terminalinde, Linux'ta ihtiyacınız yok '' -e):

sed -i '' -e 's/old text/new text/g' `grep 'old text' -rl *`

komut grep 'old text' -rl *, "eski metnin" bulunduğu çalışma dizinindeki (ve alt dizinlerdeki) tüm dosyaları listeler. Bu daha sonra sed olarak geçer.


-1

Toplu aramamı denemek / Perl betiğimi değiştirmek isteyebilirsiniz . Zincirleme yardımcı çözümlere göre bazı avantajları vardır (birden çok düzeyde kabuk meta karakter yorumlamasıyla uğraşmak zorunda kalmamak gibi).

#!/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

Klasördeki dosyaların adının bazı normal adlara sahip olması durumunda (dosya1, dosya2 ...

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

bu sorulan soruyla ilgili değil. Soru aynı dosya / klasör adı modelinden bahsetmiyor. Lütfen bu tür cevaplardan kaçının
Kunal Parekh
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.