如何查找和替换每次出现的情况:
subdomainA.example.com
和
subdomainB.example.com
在 /home/www/
目录树下的每个文本文件中递归?
find . -path ./.git -prune -o -type f -name '*matchThisText*' -print0
中的 -path ./.git -prune -o
从结果中排除目录,例如 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'
-print0
告诉 find
打印由空字符分隔的每个结果,而不是新行。万一您的目录中包含名称中带有换行符的文件,这仍然可以让 xargs
处理正确的文件名。
\( -type d -name .git -prune \)
是一个完全跳过所有名为 .git
的目录的表达式。如果您使用 SVN 或想要保留其他文件夹,您可以轻松扩展它——只需匹配更多名称即可。它大致相当于 -not -path .git
,但更有效,因为它不是检查目录中的每个文件,而是完全跳过它。由于 -prune
的实际工作方式,它后面的 -o
是必需的。
有关详细信息,请参阅 man find
。
对我来说最简单的方法是
grep -rl oldtext . | xargs sed -i 's/oldtext/newtext/g'
.svn
。例如:grep -rl oldtext . --exclude-dir=.svn | xargs sed -i 's/oldtext/newtext/g'
sed -i
导致 sed: 1: "file_path": invalid command code .
。这是因为 -i 是 macOS 上的不同标志。我发现 grep -rl old . | xargs sed -i "" -e 's/old/new/g'
有效。我发现this很有用
grep -Irl oldtext . | xargs sed -i 's/oldtext/newtext/g'
git grep -rl oldtext . | xargs sed -i 's/oldtext/newtext/g'
来避免搜索依赖项(可能会通过 .gitignore 忽略):) 很好的解决方案! @phyatt 这是一个更好的方法。
grep -rl 'SEARCHSTRING' ./ | LC_ALL=C xargs sed -i '' 's/SEARCHSTRING/REPLACESTRING/g'
注意:不要在包含 git 存储库的文件夹上运行此命令 - 更改 .git 可能会损坏您的 git 索引。
find /home/www/ -type f -exec \
sed -i 's/subdomainA\.example\.com/subdomainB.example.com/g' {} +
与此处的其他答案相比,这比大多数答案更简单,并且使用 sed 而不是 perl,这是原始问题所要求的。
-i
选项提供一个明确的空字符串 arg。即:sed -i '' 's/original/replacement/g'
grep -rl placeholder . | grep -Ev ".git" | xargs sed -i s/placeholder/lol/g
(grep -Ev 排除模式) - 提示:在实际运行它来替换它之前,首先使用它而不使用 -i
,就像试运行一样。
所有的技巧几乎都是一样的,但我喜欢这个:
find <mydir> -type f -exec sed -i 's/<string1>/<string2>/g' {} +
find
-type f:文件类型:普通文件
-exec command {} +:-exec 操作的这个变体在选定的文件上运行指定的命令,但命令行是通过在末尾附加每个选定的文件名来构建的;该命令的调用总数将远少于匹配文件的数量。命令行的构建方式与 xargs 构建其命令行的方式非常相似。命令中只允许有一个“{}”实例。该命令在起始目录中执行。
对我来说,最容易记住的解决方案是 https://stackoverflow.com/a/2113224/565525,即:
sed -i '' -e 's/subdomainA/subdomainB/g' $(find /home/www/ -type f)
注意:-i ''
解决 OSX 问题sed: 1: "...": invalid command code .
注意:如果要处理的文件太多,您将获得 Argument list too long
。解决方法 - 使用上述 find -exec
或 xargs
解决方案。
sed: can't read : No such file or directory
。为什么以及如何解决?
cd /home/www && find . -type f -print0 |
xargs -0 perl -i.bak -pe 's/subdomainA\.example\.com/subdomainB.example.com/g'
对于使用 silver searcher (ag
) 的任何人
ag SearchString -l0 | xargs -0 sed -i 's/SearchString/Replacement/g'
由于 ag 默认忽略 git/hg/svn 文件/文件夹,因此在存储库中运行是安全的。
一个不错的 oneliner 作为额外的。使用 git grep。
git grep -lz 'subdomainA.example.com' | xargs -0 perl -i'' -pE "s/subdomainA.example.com/subdomainB.example.com/g"
refactor() { echo "Replacing $1 by $2 in all files in this git repository." git grep -lz $1| xargs -0 perl -i'' -pE "s/$1/$2/g" }
用法,例如将 'word' 替换为 'sword':refactor word sword
然后验证它对 git diff
做了什么。
这个与 git 存储库兼容,并且更简单一些:
Linux:
git grep -l 'original_text' | xargs sed -i 's/original_text/new_text/g'
苹果电脑:
git grep -l 'original_text' | xargs sed -i '' -e 's/original_text/new_text/g'
(感谢http://blog.jasonmeridth.com/posts/use-git-grep-to-replace-strings-in-files-in-your-git-repository/)
git-grep
的 -z
选项与 xargs -0
一起使用更明智。
git grep
显然只在 git
存储库中才有意义。一般替换为 grep -r
。
-z
,git-grep
将用空字节而不是换行符分隔输出字段;并且使用 -0
,xargs
将读取由空字节分隔的输入,而不是空格(并且不会用引号做奇怪的事情)。因此,如果您不想在文件名包含空格、引号或其他有趣字符时中断命令,则命令为:git grep -z -l 'original_text' | xargs -0 sed ...
。
要通过递归 sed
减少文件,您可以为您的字符串实例 grep
:
grep -rl <oldstring> /path/to/folder | xargs sed -i s^<oldstring>^<newstring>^g
如果您运行 man grep
,您会注意到如果您想省略搜索 .git 目录,您还可以定义一个 --exlude-dir="*.git"
标志,避免其他人礼貌指出的 git 索引问题。
带领您:
grep -rl --exclude-dir="*.git" <oldstring> /path/to/folder | xargs sed -i s^<oldstring>^<newstring>^g
最简单的替换方法(所有文件、目录、递归)
find . -type f -not -path '*/\.*' -exec sed -i 's/foo/bar/g' {} +
注意: 有时您可能需要忽略一些隐藏文件,即.git
,您可以使用上述命令。
如果要包含隐藏文件使用,
find . -type f -exec sed -i 's/foo/bar/g' {} +
在这两种情况下,字符串 foo
都将替换为新字符串 bar
如果您需要排除目录 (--exclude-dir=..folder
) 并且可能有带空格的文件名(通过对 grep -Z
和 {3 使用 0Byte 来解决),这是一种直接的方法})
grep -rlZ oldtext . --exclude-dir=.folder | xargs -0 sed -i 's/oldtext/newtext/g'
find /home/www/ -type f -exec perl -i.bak -pe 's/subdomainA\.example\.com/subdomainB.example.com/g' {} +
find /home/www/ -type f
将列出 /home/www/(及其子目录)中的所有文件。 “-exec”标志告诉 find 在找到的每个文件上运行以下命令。
perl -i.bak -pe 's/subdomainA\.example\.com/subdomainB.example.com/g' {} +
是在文件上运行的命令(一次很多)。 {}
被文件名替换。命令末尾的 +
告诉 find
为多个文件名构建一个命令。
根据 find
手册页:“命令行的构建方式与 xargs 构建其命令行的方式非常相似。”
因此,无需使用 xargs -0
或 -print0
即可实现您的目标(并处理包含空格的文件名)。
我只是需要这个并且对可用示例的速度不满意。所以我想出了我自己的:
cd /var/www && ack-grep -l --print0 subdomainA.example.com | xargs -0 perl -i.bak -pe 's/subdomainA\.example\.com/subdomainB.example.com/g'
Ack-grep 在查找相关文件方面非常有效。这个命令轻而易举地替换了 ~145 000 个文件,而其他命令花了很长时间,我等不及他们完成了。
grep -ril 'subdomainA' *
远没有 grep -Hr 'subdomainA' * | cut -d: -f1
快。
或使用速度极快的 GNU Parallel:
grep -rl oldtext . | parallel sed -i 's/oldtext/newtext/g' {}
sudo pacman -S parallel
; ubuntu/debian:sudo apt-get install parallel
;软呢帽:dnf install parallel
;我用拱顺便说一句
尝试这个:
sed -i 's/subdomainA/subdomainB/g' `grep -ril 'subdomainA' *`
sed -i 's/subdomainA/subdomainB/g'
` grep -ril 'subdomainA' /home/www/*
` - 这看起来仍然不太好了,但应该在复制粘贴中幸存下来:) 干杯!
grep -lr 'subdomainA.example.com' | while read file; do sed -i "s/subdomainA.example.com/subdomainB.example.com/g" "$file"; done
我想大多数人不知道他们可以将某些内容通过管道传输到“同时读取文件”中,并且它避免了那些讨厌的 -print0 参数,同时保留了文件名中的空格。
在 sed 之前进一步添加 echo
可以让您在实际执行之前查看哪些文件会更改。
-print0
有用的原因是它可以处理 while read
根本无法处理的情况——换行符是 Unix 文件名中的有效字符,因此要使您的代码完全健壮,它需要处理这样的文件名, 也。 (此外,您希望 read -r
避免在 read
中出现一些讨厌的 POSIX 遗留行为。)
sed
是空操作,因此 grep
不是必需的;虽然它是一个有用的优化,可以避免重写不包含任何匹配项的文件,如果您有很多匹配项,或者想要避免不必要地更新文件上的日期戳。
#!/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
您可以使用 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
希望对你有帮助 !!!
sed
的命令都会失败,即使使用 osx 特定设置也是如此。
根据 this 博文:
find . -type f | xargs perl -pi -e 's/oldtext/newtext/g;'
/
?例如,我想替换 IP 地址:xxx.xxx.xxx.xxx
为 xxx.xxx.xxx.xxx/folder
/
。例如:find . -type f | xargs perl -pi -e 's/xxx.xxx.xxx.xxx\/folder/newtext/g;'
如果您不介意将 vim
与 grep
或 find
工具一起使用,您可以跟进用户 Gert 在此链接中给出的答案 --> How to do a text replacement in a big folder hierarchy?。
这是交易:
递归 grep 查找要在某个路径中替换的字符串,并且只取匹配文件的完整路径。 (那将是 $(grep 'string' 'pathname' -Rl)。
(可选)如果您想在集中目录上对这些文件进行预备份,也许您也可以使用它: cp -iv $(grep 'string' 'pathname' -Rl) 'centralized-directory-pathname'
之后,您可以按照类似于给定链接上提供的方案在 vim 中随意编辑/替换: :bufdo %s#string#replacement#gc |更新
:bufdo %s#string#replacement#gc |更新
有点老派,但这适用于 OS X。
有几个技巧:
• 将仅编辑当前目录下扩展名为 .sls
的文件
• .
必须转义以确保 sed
不会将它们评估为“任何字符”
• ,
用作 sed
分隔符,而不是通常的 /
另请注意,这是编辑 Jinja 模板以在 import
的路径中传递 variable
(但这是题外话)。
首先,验证您的 sed 命令是否符合您的要求(这只会将更改打印到标准输出,不会更改文件):
for file in $(find . -name *.sls -type f); do echo -e "\n$file: "; sed 's,foo\.bar,foo/bar/\"+baz+\"/,g' $file; done
准备好进行更改后,根据需要编辑 sed 命令:
for file in $(find . -name *.sls -type f); do echo -e "\n$file: "; sed -i '' 's,foo\.bar,foo/bar/\"+baz+\"/,g' $file; done
请注意 sed 命令中的 -i ''
,我不想创建原始文件的备份(如 In-place edits with sed on OS X 或 Robert Lujo 在本页的评论中所述)。
快乐的seding伙计们!
要替换 git 存储库中的所有匹配项,您可以使用:
git ls-files -z | xargs -0 sed -i 's/subdomainA\.example\.com/subdomainB.example.com/g'
有关列出存储库中所有文件的其他选项,请参阅 List files in local git repo?。 -z
选项告诉 git 用零字节分隔文件名,这确保 xargs
(带有选项 -0
)可以分隔文件名,即使它们包含空格或诸如此类的东西。
只是为了避免改变
NearlysubdomainA.example.com
subdomainA.example.comp.other
但仍然
子域A.example.com.IsIt.good
(在域根背后的想法可能不好)
find /home/www/ -type f -exec sed -i 's/\bsubdomainA\.example\.com\b/\1subdomainB.example.com\2/g' {} \;
我只使用上衣:
find . -name '*.[c|cc|cp|cpp|m|mm|h]' -print0 | xargs -0 tops -verbose replace "verify_noerr(<b args>)" with "__Verify_noErr(<args>)" \
replace "check(<b args>)" with "__Check(<args>)"
这是一个比大多数版本更通用的版本;例如,它不需要 find
(使用 du
)。它确实需要 xargs
,这仅在 Plan 9 的某些版本(如 9front)中提供。
du -a | awk -F' ' '{ print $2 }' | xargs sed -i -e 's/subdomainA\.example\.com/subdomainB.example.com/g'
如果您想添加文件扩展名等过滤器,请使用 grep
:
du -a | grep "\.scala$" | awk -F' ' '{ print $2 }' | xargs sed -i -e 's/subdomainA\.example\.com/subdomainB.example.com/g'
对于 IBMi 上的 Qshell (qsh),而不是 OP 标记的 bash。
qsh 命令的限制:
find 没有 -print0 选项
xargs 没有 -0 选项
sed 没有 -i 选项
因此qsh中的解决方案:
PATH='your/path/here'
SEARCH=\'subdomainA.example.com\'
REPLACE=\'subdomainB.example.com\'
for file in $( find ${PATH} -P -type f ); do
TEMP_FILE=${file}.${RANDOM}.temp_file
if [ ! -e ${TEMP_FILE} ]; then
touch -C 819 ${TEMP_FILE}
sed -e 's/'$SEARCH'/'$REPLACE'/g' \
< ${file} > ${TEMP_FILE}
mv ${TEMP_FILE} ${file}
fi
done
注意事项:
解决方案不包括错误处理
不是 OP 标记的 Bash
for
行方面存在一些令人讨厌的问题。
如果您想在不完全破坏您的 SVN 存储库的情况下使用它,您可以通过执行以下操作告诉“查找”忽略所有隐藏文件:
find . \( ! -regex '.*/\..*' \) -type f -print0 | xargs -0 sed -i 's/subdomainA.example.com/subdomainB.example.com/g'
使用 grep
和 sed
的组合
for pp in $(grep -Rl looking_for_string)
do
sed -i 's/looking_for_string/something_other/g' "${pp}"
done
grep -Rl pattern
的输出生成了模式所在的文件列表。 for
循环中不读取文件。
for
循环;如果任何返回的文件名包含空格,它将无法正常工作,因为 shell 标记了 for
参数列表。但是随后您在循环内使用不带引号的文件名变量,因此如果您修复此问题,它将在那里中断。纠正这些剩余的错误将使您的错误与@MadMan2064 的答案相同。
perl -p -i -e 's/oldthing/new_thingy/g' `grep -ril oldthing *`
awk
/sed
,但 perl 很常见(嵌入式/系统除外)。
subdomainA\.example\.com
值的点被转义了,而第二个sudomainB.example.com
值却没有?我以建议的格式执行它,它似乎完美地完成了这项工作,但我很好奇为什么只为第一个字符串模式呈现转义。Permission denied
结束。最好使用-exec sed -i ... {} \;
而不是管道。find . -type f -print0 | xargs -0 sed -i -e 's/\r$//'
以递归方式将特定目录中的文件中的所有 CRLF 替换为 LF。find . \( ! -regex '.*/\..*' \) -type f | LC_ALL=C xargs sed -i '' 's/foo/bar/g'