如何使用awk或sed进行递归查找/替换字符串?

Ted*_*edd 642 bash awk replace sed

如何找到并替换每次出现的事件:

subdomainA.example.com
Run Code Online (Sandbox Code Playgroud)

subdomainB.example.com
Run Code Online (Sandbox Code Playgroud)

/home/www/目录树下的每个文本文件递归?

Nik*_*hev 817

注意:不要在包含git repo的文件夹上运行此命令 - 更改为.git可能会损坏您的git索引.

find /home/www \( -type d -name .git -prune \) -o -type f -print0 | xargs -0 sed -i 's/subdomainA\.example\.com/subdomainB.example.com/g'
Run Code Online (Sandbox Code Playgroud)

来自-print0:

-print0 (仅限GNU查找)告诉find使用空字符(\ 0)而不是空格作为找到的路径名之间的输出分隔符.如果文件可以包含空格或其他特殊字符,则这是一个更安全的选项.建议使用-print0参数来查找是否使用-exec命令或xargs(xargs中需要-0参数.).

  • 在OSX上,您可能会遇到`sed:1:"...":无效的命令代码.`问题.似乎-i选项需要扩展并解析`s/../...'`命令.解决方案:将扩展名''传递给-i选项,如`sed -i''s/...`. (131认同)
  • 另外,如果你是一个git仓库,要小心.我认为我在一个明确的分支上测试它是聪明的,所以如果它做了坏事,我可以恢复,但反而损坏了我的git索引. (54认同)
  • 使用这个`grep -r'hello'-l --null.| xargs -0 sed -i#hello#world#g'`以避免编辑不相关的文件(sed可能会更改文件编码). (11认同)
  • 注意:如果你在一个目录上使用它,并想知道为什么`svn st`没有显示任何变化,那是因为你已经修改了.svn目录中的文件!使用`find.-maxdepth 1-type f -print0 | xargs -0 sed -i's/toreplace/replacement/g'`而不是. (6认同)
  • "但反而损坏了我的git索引." 不要太担心这个,你可以做`find .git ... | ...'sed -is /(与之前相反)/ g'`来修复你的git索引 (6认同)
  • 在OSX上,您还可以"port install gsed"(GNU sed),然后将"sed"替换为"gsed"以获取GNU版本. (4认同)
  • 忽略隐藏的dirs:`find.-not -path'*/\.*' - type f -print0 | xargs -0 sed -i's/subdomainA.example.com/subdomainB.example.com/g'` (4认同)
  • 这对我有用,我的案例是查找/替换 IP 地址值。不过,画廊的问题是:为什么第一个“subdomainA\.example\.com”值的点被转义,但第二个“sudomainB.example.com”值的点却没有转义?我按照建议的格式执行了它,它似乎完美地完成了工作,但我很好奇为什么只针对第一个字符串模式提供转义。 (3认同)
  • 如果其中一个文件具有不可变标志,则此脚本将停止,而不会到达末尾,并出现错误“权限被拒绝”。最好使用 `-exec sed -i ... {} \;` 而不是管道。 (2认同)
  • @elrobis(12 年后,但为了记录)第一个 URL 使用转义点,因为它位于正则表达式匹配文本中并且很特殊,但第二个 URL 位于替换文本中并且点在该上下文中并不特殊。 (2认同)

Joh*_*nck 242

注意:不要在包含git repo的文件夹上运行此命令 - 更改为.git可能会损坏您的git索引.

find /home/www/ -type f -exec \
    sed -i 's/subdomainA\.example\.com/subdomainB.example.com/g' {} +
Run Code Online (Sandbox Code Playgroud)

与此处的其他答案相比,这比大多数答案更简单,并使用sed而不是perl,这是原始问题所要求的.

  • 请注意,如果您正在使用BSD sed(包括在Mac OS X上),则需要为sed的`-i`选项提供一个明确的空字符串arg.即:`sed -i''s/original/replacement/g'` (49认同)
  • 如果从查找结果中排除repo,则可以安全地在包含git repo的文件夹上执行:`find.-not -path'*/\.git*' - type f ...`. (15认同)
  • @AoeAoe:`+`大大减少了生成的`sed`进程的数量.它效率更高. (6认同)
  • 如何在带有git repo的文件夹中安全地执行此操作? (4认同)
  • @JohnZwinck我的错误,错过了+.奇怪的是,尼基塔的解决方案对我来说运行得更快. (2认同)
  • 如果任何子目录包含空格,这将不起作用.它应该是`find/home/www/-type f -exec sed -i's/subdomainA\.example\.com/subdomainB.example.com/g'"{}"+`注意周围的双引号大括号. (2认同)
  • 当然,但使用`find`来调用`xargs`来调用`sed`比使用`find`来调用`sed`要低很多.这是最好的答案,并且在大括号周围使用双引号会更好. (2认同)

Ana*_*oly 173

对我来说最简单的方法是

grep -rl oldtext . | xargs sed -i 's/oldtext/newtext/g'
Run Code Online (Sandbox Code Playgroud)

  • 当你需要排除目录时,这很有效,比如`.svn`.例如:`grep -rl oldtext.--exclude-dir = .svn | xargs sed -i's/oldtext/newtext/g'` (28认同)
  • 在 macOS 上, `sed -i` 会导致 `sed: 1: "file_path": invalid command code .`。这是因为 -i 在 macOS 上是一个不同的标志。我发现`grep -rl old 。| xargs sed -i "" -e 's/old/new/g'` 有效。我发现[这个](/sf/ask/1361956291/)很有用 (14认同)
  • `brew install gnu-sed`并在OSX上使用`gsed`来避免痛苦的世界. (9认同)
  • 我发现你可以在 grep 中添加“-Z”,在 xargs 中添加“-0”来捕获带空格的文件名:“grep -rlZ oldtext”。| xargs -0 sed -i 's/oldtext/newtext/g'`:/sf/ask/1210756781/ (6认同)
  • 如果您使用编译语言并希望避免检查二进制文件,您可以传递 I 标志,例如 `grep -Irl oldtext 。| xargs sed -i 's/oldtext/newtext/g'` (5认同)
  • 实际上效率不同于其他在这里评价很高的答案 (4认同)
  • @ user2284570使用`-I`或`--binary-file = without-match` grep标志. (3认同)
  • 在 git 项目中,请确保使用 `git grep -rl oldtext 。| xargs sed -i 's/oldtext/newtext/g'` 以避免搜索依赖项(可能通过 .gitignore 忽略):) 很好的解决方案!@phyatt这是一个更好的方法。 (2认同)
  • 使用 MACOS 并沮丧为什么它不起作用 -> 尝试 -> `grep -rl 'SEARCHSTRING' ./ | LC_ALL=C xargs sed -i '' 's/SEARCHSTRING/REPLACESTRING/g' ` (2认同)

I15*_*159 59

所有技巧几乎相同,但我喜欢这个:

find <mydir> -type f -exec sed -i 's/<string1>/<string2>/g' {} +
Run Code Online (Sandbox Code Playgroud)
  • find <mydir>:在目录中查找.

  • -type f:

    文件类型为:常规文件

  • -exec command {} +:

    -exec操作的此变体在所选文件上运行指定的命令,但命令行是通过在末尾附加每个选定的文件名来构建的; 命令的调用总数将远远少于匹配文件的数量.命令行的构建方式与xargs构建命令行的方式大致相同.命令中只允许一个"{}"实例.该命令在起始目录中执行.

  • @ I159这个答案与[John Zwinck的](http://stackoverflow.com/a/1585810/3076724)不一样吗? (7认同)

Emp*_*ian 39

cd /home/www && find . -type f -print0 |
  xargs -0 perl -i.bak -pe 's/subdomainA\.example\.com/subdomainB.example.com/g'
Run Code Online (Sandbox Code Playgroud)

  • 有:来自"man find":为每个匹配的文件运行一次指定的命令.也就是说,如果/ home/www中有2000个文件,那么'find ... -exec ...'将导致2000个perl调用; 而'找到... | xargs ...'只会调用perl一次或两次(假设ARG_MAX约为32K,平均文件名长度为20). (4认同)
  • @EmployedRussian,`find -exec ... {} +`自2006年以来一直是POSIX指定的. (4认同)
  • 我很好奇,是否有理由使用`-print0`和`xargs`而不是`-exec`或`-execdir`? (2认同)
  • @Employed Russian:这就是你使用`find -exec command {} +`的原因 - 它确实避免了像xargs这样的命令的过度调用,但没有单独的进程. (2认同)
  • 在哪个平台上?xargs解决方案是可移植的,"find ... -exec"的"神奇"调用不会为找到的每个文件调用子进程. (2认同)

Rob*_*ujo 30

对我来说,最简单的解决方案是/sf/answers/147925711/,即:

sed -i '' -e 's/subdomainA/subdomainB/g' $(find /home/www/ -type f)
Run Code Online (Sandbox Code Playgroud)

注意:-i ''解决OSX问题sed: 1: "...": invalid command code .

注意:如果要处理的文件太多,您将获得Argument list too long.解决方法 - 使用find -execxargs解决方案如上所述.

  • 在所有情况下,`workaround`应该是首选语法. (4认同)
  • 在 Cygwin 上,它会生成“sed:无法读取:没有这样的文件或目录”。为什么以及如何解决? (2认同)

Jac*_*ang 26

任何人使用银色搜索者(ag)

ag SearchString -l0 | xargs -0 sed -i 's/SearchString/Replacement/g'
Run Code Online (Sandbox Code Playgroud)

由于ag默认忽略git/hg/svn文件/文件夹,因此可以安全地在存储库中运行.


dom*_*gia 15

要以递归sed方式减少文件,可以grep使用字符串实例:

grep -rl <oldstring> /path/to/folder | xargs sed -i s^<oldstring>^<newstring>^g
Run Code Online (Sandbox Code Playgroud)

如果你运行man grep你会发现你也可以定义一个--exlude-dir="*.git"标志,如果你想省略搜索.git目录,避免git索引问题,正如其他人礼貌地指出的那样.

引导您:

grep -rl --exclude-dir="*.git" <oldstring> /path/to/folder | xargs sed -i s^<oldstring>^<newstring>^g
Run Code Online (Sandbox Code Playgroud)


Jim*_*ane 14

一个不错的oneliner作为额外的.使用git grep.

git grep -lz 'subdomainA.example.com' | xargs -0 perl -i'' -pE "s/subdomainA.example.com/subdomainB.example.com/g"
Run Code Online (Sandbox Code Playgroud)

  • 如果你没有冒险覆盖.git/contents的风险,那么在git repo中工作是个好主意(如另一个答案的评论中所述). (3认同)

sed*_*nym 12

这个与git存储库兼容,有点简单:

Linux的:

git grep -l 'original_text' | xargs sed -i 's/original_text/new_text/g'
Run Code Online (Sandbox Code Playgroud)

苹果电脑:

git grep -l 'original_text' | xargs sed -i '' -e 's/original_text/new_text/g'
Run Code Online (Sandbox Code Playgroud)

(感谢http://blog.jasonmeridth.com/posts/use-git-grep-to-replace-strings-in-files-in-your-git-repository/)

  • @PetrPeller:使用`-z`,`git-grep`将输出字段用空字节而不是换行分隔; 并且使用`-0`,`xargs`将读取由空字节分隔的输入,而不是空白(而不是用引号做奇怪的东西).因此,如果您不希望命令在文件名包含空格,引号或其他有趣字符时中断,则命令为:`g​​it grep -z -l'origative_text'| xargs -0 sed ...`. (2认同)

Saz*_*han 11

最简单的替换方法(所有文件、目录、递归

find . -type f -not -path '*/\.*' -exec sed -i 's/foo/bar/g' {} +
Run Code Online (Sandbox Code Playgroud)

注意:有时您可能需要忽略一些隐藏文件,即.git,您可以使用上面的命令。

如果要包含隐藏文件,请使用,

find . -type f  -exec sed -i 's/foo/bar/g' {} +
Run Code Online (Sandbox Code Playgroud)

在这两种情况下,字符串foo都将替换为新字符串bar


unu*_*tbu 9

find /home/www/ -type f -exec perl -i.bak -pe 's/subdomainA\.example\.com/subdomainB.example.com/g' {} +
Run Code Online (Sandbox Code Playgroud)

find /home/www/ -type f 将列出/ home/www /(及其子目录)中的所有文件."-exec"标志告诉find在找到的每个文件上运行以下命令.

perl -i.bak -pe 's/subdomainA\.example\.com/subdomainB.example.com/g' {} +
Run Code Online (Sandbox Code Playgroud)

是对文件运行的命令(一次多个).该{}被按文件名称进行替换.将+在命令的末尾告诉find给了很多文件名建立一个命令.

根据find手册页:"命令行的构建方式与xargs构建命令行的方式大致相同."

因此,可以在不使用xargs -0或的情况下实现您的目标(并处理包含空格的文件名)-print0.


小智 7

尝试这个:

sed -i 's/subdomainA/subdomainB/g' `grep -ril 'subdomainA' *`
Run Code Online (Sandbox Code Playgroud)


Hen*_*nno 7

我只需要这个并且对可用示例的速度不满意.所以我想出了自己的:

cd /var/www && ack-grep -l --print0 subdomainA.example.com | xargs -0 perl -i.bak -pe 's/subdomainA\.example\.com/subdomainB.example.com/g'
Run Code Online (Sandbox Code Playgroud)

Ack-grep在查找相关文件方面非常有效.这个命令轻而易举地取代了~14.5万个文件,而其他文件花费了很长时间,我不能等到它们完成.


J.H*_*our 7

根据这篇博文:

find . -type f | xargs perl -pi -e 's/oldtext/newtext/g;'
Run Code Online (Sandbox Code Playgroud)


mic*_*oo8 6

或使用极快的 GNU Parallel:

grep -rl oldtext . | parallel sed -i 's/oldtext/newtext/g' {}
Run Code Online (Sandbox Code Playgroud)


小智 5

#!/usr/local/bin/bash -x

find * /home/www -type f | while read files
do

sedtest=$(sed -n '/^/,/$/p' "${files}" | sed -n '/subdomainA/p')

    if [ "${sedtest}" ]
    then
    sed s'/subdomainA/subdomainB/'g "${files}" > "${files}".tmp
    mv "${files}".tmp "${files}"
    fi

done
Run Code Online (Sandbox Code Playgroud)


小智 5

grep -lr 'subdomainA.example.com' | while read file; do sed -i "s/subdomainA.example.com/subdomainB.example.com/g" "$file"; done

我想大多数人不知道他们可以将某些内容通过管道传输到“同时读取文件”中,并且它避免了那些讨厌的 -print0 参数,同时保留文件名中的空格。

echo在 sed 之前进一步添加一个允许您在实际执行之前查看哪些文件将更改。


sar*_*mar 5

您可以使用 awk 来解决这个问题,如下所示,

for file in `find /home/www -type f`
do
   awk '{gsub(/subdomainA.example.com/,"subdomainB.example.com"); print $0;}' $file > ./tempFile && mv ./tempFile $file;
done
Run Code Online (Sandbox Code Playgroud)

希望能帮到你 !!!


ine*_*tom 5

如果您需要排除目录--exclude-dir=.svn)并且文件名中带有空格(使用0Byte和grep -Zandxargs -0

grep -rlZ oldtext . --exclude-dir=.svn | xargs -0 sed -i 's/oldtext/newtext/g'
Run Code Online (Sandbox Code Playgroud)


归档时间:

查看次数:

487924 次

最近记录:

5 年,9 月 前