sed初学者:更改文件夹中的所有匹配项


98

我需要做一个正则表达式查找并替换文件夹(及其子文件夹)中的所有文件。linux shell命令将执行该操作吗?

例如,我要在所有文件上运行此文件,并用替换后的新文本覆盖旧文件。

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

Answers:


148

没有办法仅使用sed做到这一点。您至少需要一起使用find实用程序:

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

此命令将为.bak每个更改的文件创建一个文件。

笔记:

  • command 的-i参数sed是GNU扩展,因此,如果您正在与BSD一起运行此命令,sed则需要将输出重定向到新文件,然后重命名。
  • find实用程序不会-exec在旧的UNIX框中实现该参数,因此,您将需要使用a | xargs代替。

4
\;为了什么
安德里·马库卡

4
我们需要告诉查找参数-exec的命令以“;”结尾。但是外壳程序使用与外壳程序命令分隔符相同的符号(;),因此,我们需要对“;”进行转义。从外壳传递给find的-exec参数。
osantana

2
值得注意的是,-i它本身不会创建备份文件,而是导致sed在适当位置执行文件操作的原因。
凯尔(Kyle)

1
{}为了什么?
昵称

1
{}会由发现的每个文件名进行更换find,并\;告诉找到的命令,他需要在这一点上,执行完成。
osantana

51

我更喜欢使用find | xargs cmdfind -exec因为它更容易记住。

此示例在当前目录或以下目录的.txt文件中用“ bar”全局替换“ foo”:

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

为了可移植性,我不依赖sed特定于linux或BSD的功能。取而代之的是,我使用overwriteKernighan和Pike在Unix编程环境上的书中的脚本。

该命令是

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

因为Shell会截断文件,然后sed才能读取文件。


3

我建议(备份文件后):

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

0

示例:将/ app / config /文件夹及其子文件夹下的所有ini文件的{AutoStart}替换为1:

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
请解释您的答案。
退学

尽管此代码可以解决OP的问题,但最好包含有关您的代码如何解决OP的问题的说明。这样,将来的访问者可以从您的帖子中学习,并将其应用于自己的代码。SO不是编码服务,而是知识资源。高质量,完整的答案强化了这一想法,并且更有可能被否决。这些功能,再加上所有职位必须自成体系的要求,是SO作为与众不同的平台的优势。您可以进行编辑以添加其他信息和/或在源文档中补充说明。
SherylHohman

1
如果您看不懂,请忘记我的回答。这只是bash的基础知识。
6

-1

可能想尝试我的大量搜索/替换Perl脚本。与链式实用程序解决方案相比,它具有一些优势(例如,不必处理多个级别的外壳元字符解释)。

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

这与提出的问题无关。该问题未提及有关相同文件/文件夹名称模式的任何内容。请避免这样的答案
Kunal Parekh
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.