Linux sed 初学者:更改文件夹中的所有事件

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/905144/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-03 17:20:57  来源:igfitidea点击:

sed beginner: changing all occurrences in a folder

regexlinuxshellsed

提问by nickf

I need to do a regex find and replace on all the files in a folder (and its subfolders). What would be the linux shell command to do that?

我需要对文件夹(及其子文件夹)中的所有文件进行正则表达式查找和替换。什么是 linux shell 命令来做到这一点?

For example, I want to run this over all the files and overwrite the old file with the new, replaced text.

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

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

采纳答案by osantana

There is no way to do it using only sed. You'll need to use at least the find utility together:

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

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

This command will create a .bakfile for each changed file.

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

Notes:

笔记:

  • The -iargument for sedcommand is a GNU extension, so, if you are running this command with the BSD's sedyou will need to redirect the output to a new file then rename it.
  • The findutility does not implement the -execargument in old UNIX boxes, so, you will need to use a | xargsinstead.
  • command的-i参数sed是 GNU 扩展,因此,如果您使用 BSD 运行此命令,sed则需要将输出重定向到一个新文件,然后重命名它。
  • find实用程序不会-exec在旧的 UNIX 机器中实现该参数,因此,您需要使用 a| xargs来代替。

回答by paxdiablo

Might I suggest (after backing up your files):

我可以建议(备份文件后):

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

回答by chaos

Might want to try my mass search/replace Perl script. Has some advantages over chained-utility solutions (like not having to deal with multiple levels of shell metacharacter interpretation).

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

#!/usr/bin/perl

use strict;

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

die "Usage: 
find /the/folder -type f -exec overwrite '{}' sed 's/old/new/g' {} ';'
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"; }

回答by Norman Ramsey

For portability, I don't rely on features of sed that are specific to linux or BSD. Instead I use the overwritescript from Kernighan and Pike's book on the Unix Programming Environment.

为了可移植性,我不依赖特定于 linux 或 BSD 的 sed 功能。相反,我使用overwriteKernighan 和 Pike 关于 Unix 编程环境的书中的脚本。

The command is then

然后命令是

#!/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=; 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:  failed, $file unchanged" 1>&2
    exit 1
fi
rm -f $new $old

And the overwritescript (which I use all over the place) is

overwrite脚本(这是我使用所有的地方)是

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

The idea is that it overwrites a file only if a command succeeds. Useful in findand also where you would not want to use

这个想法是只有在命令成功时它才会覆盖文件。find在您不想使用的地方以及在您不想使用的地方很有用

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

because the shell truncates the file before sedcan read it.

因为外壳在sed读取文件之前会截断文件。

回答by Dennis

I prefer to use find | xargs cmdover find -execbecause it's easier to remember.

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

This example globally replaces "foo" with "bar" in .txt files at or below your current directory:

此示例在当前目录或低于当前目录的 .txt 文件中将“foo”全局替换为“bar”:

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

The -print0and -0options can be left out if your filenames do not contain funky characters such as spaces.

-print0-0选项可以被排除在外,如果你的文件名不包含时髦的字符,如空格。

回答by tereza

In case the name of files in folder has some regular names (like file1, file2...) I have used for cycle.

如果文件夹中的文件名有一些常规名称(如 file1、file2...),我已用于循环。

##代码##