如何从命令行进行递归查找和替换?

Nat*_*ong 120 bash zsh shell find-and-replace

使用像 bash 或 zshell 这样的 shell,如何进行递归的“查找和替换”?换句话说,我想在这个目录及其子目录中的所有文件中用 'bar' 替换每次出现的 'foo'。

Nat*_*ong 150

此命令将执行此操作(在 Mac OS X Lion 和 Kubuntu Linux 上测试)。

# Recursively find and replace in files
find . -type f -name "*.txt" -print0 | xargs -0 sed -i '' -e 's/foo/bar/g'
Run Code Online (Sandbox Code Playgroud)

这是它的工作原理:

  1. find . -type f -name '*.txt'在当前目录 ( .) 及以下目录中查找-type f名称以.txt
  2. | 将该命令的输出(文件名列表)传递给下一个命令
  3. xargs 收集这些文件名并一一交给 sed
  4. sed -i '' -e 's/foo/bar/g'表示“就地编辑文件,无需备份,并在s/foo/bar每行 ( /g) 中多次进行以下替换( )”(请参阅man sed

请注意,第 4 行中的“无备份”部分对我来说是可以的,因为无论如何我要更改的文件都在版本控制之下,因此如果出现错误,我可以轻松撤消。

为了避免记住这一点,我使用了一个交互式 bash 脚本,如下所示:

#!/bin/bash
# find_and_replace.sh

echo "Find and replace in current directory!"
echo "File pattern to look for? (eg '*.txt')"
read filepattern
echo "Existing string?"
read existing
echo "Replacement string?"
read replacement
echo "Replacing all occurences of $existing with $replacement in files matching $filepattern"

find . -type f -name $filepattern -print0 | xargs -0 sed -i '' -e "s/$existing/$replacement/g"
Run Code Online (Sandbox Code Playgroud)

  • 此外,只需 `find -name '*.txt' -exec sed -i 's/foo/bar/g' {} +` 即可使用 GNU find 完成所有这些。 (21认同)
  • 永远不要在没有 `-print0` 选项的情况下通过管道查找输出到 xargs。您的命令将在名称中带有空格等的文件上失败。 (13认同)
  • 如果我删除 `-i` 和 `''` 之间的空格,这对我有用 (12认同)
  • 当我运行 `find 时,我得到 `sed: can't read : No such file or directory`。-name '*.md' -print0 | xargs -0 sed -i '' -e 's/ä/ä/g'`,但是 `find . -name '*.md' -print0` 给出了许多文件的列表。 (8认同)
  • `sed -i`后面的`''`是什么意思`''`是什么作用? (4认同)
  • `sed:无法读取:没有这样的文件或目录` (2认同)

Zty*_*tyx 41

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

这消除了xargs依赖性。

  • 这不适用于 GNU `sed`,因此在大多数系统上都会失败。GNU `sed` 要求​​你在 `-i` 和 `''` 之间没有空格。 (4认同)
  • 接受的答案在解释它方面做得更好。但 +1 表示使用正确的语法。 (2认同)

Dav*_*cki 16

如果您使用的是 Git,那么您可以这样做:

git grep -lz foo | xargs -0 sed -i '' -e 's/foo/bar/g'
Run Code Online (Sandbox Code Playgroud)

-l只列出文件名。-z在每个结果后打印一个空字节。

我最终这样做是因为项目中的某些文件在文件末尾没有换行符,并且 sed 即使没有进行其他更改也添加了换行符。(不评论文件末尾是否应该有换行符。)

  • 这个解决方案大+1。其余的`find ... -print0 | xargs -0 sed ...` 解决方案不仅需要更长的时间,而且还会向还没有换行符的文件添加换行符,这在 git 存储库中工作时会很麻烦。相比之下,`git grep` 速度很快。 (2认同)

nex*_*ayq 8

尝试:

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

在 Ubuntu 12.04 上测试。

编辑:

如果子目录名称和/或文件名包含空格,则此命令将不起作用,但如果您确实有它们,请不要使用此命令,因为它不起作用。

在目录名和文件名中使用空格通常是一种不好的做法。

http://linuxcommand.org/lc3_lts0020.php

查看“有关文件名的重要事实”