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

时间:2009-10-17 21:10:42

标签: bash sed awk replace

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

subdomainA.example.com

subdomainB.example.com

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

35 个答案:

答案 0 :(得分:782)

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

find /home/www -type f -print0 | xargs -0 sed -i 's/subdomainA\.example\.com/subdomainB.example.com/g'

来自man find

  

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

答案 1 :(得分:227)

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

find /home/www/ -type f -exec \
    sed -i 's/subdomainA\.example\.com/subdomainB.example.com/g' {} +

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

答案 2 :(得分:141)

最简单的方法是

grep -rl oldtext . | xargs sed -i 's/oldtext/newtext/g'

答案 3 :(得分:56)

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

find <mydir> -type f -exec sed -i 's/<string1>/<string2>/g' {} +
  • find <mydir>:在目录中查找。

  • -type f

      

    文件类型为:常规文件

  • -exec command {} +

      

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

答案 4 :(得分:39)

cd /home/www && find . -type f -print0 |
  xargs -0 perl -i.bak -pe 's/subdomainA\.example\.com/subdomainB.example.com/g'

答案 5 :(得分:29)

对我而言,最容易记住的解决方案是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 -execxargs解决方案。

答案 6 :(得分:24)

对于使用silver searcherag

的任何人
ag SearchString -l0 | xargs -0 sed -i 's/SearchString/Replacement/g'

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

答案 7 :(得分:15)

要将文件减少到递归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

答案 8 :(得分:14)

一个好的oneliner作为额外的。使用git grep。

git grep -lz 'subdomainA.example.com' | xargs -0 perl -i'' -pE "s/subdomainA.example.com/subdomainB.example.com/g"

答案 9 :(得分:11)

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

Linux的:

git grep -l 'original_text' | xargs sed -i 's/original_text/new_text/g'

的Mac:

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/

答案 10 :(得分:8)

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的情况下实现目标(并处理包含空格的文件名)。

答案 11 :(得分: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'

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

答案 12 :(得分:6)

或使用快速的GNU Parallel:

grep -rl oldtext . | parallel sed -i 's/oldtext/newtext/g' {}

答案 13 :(得分:4)

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

我想大多数人都不知道他们可以将某些东西输入“while read file”中,并且它避免了那些令人讨厌的-print0 args,同时预先设置了文件名中的空格。

在sed之前进一步添加echo可以让您在实际执行之前查看哪些文件会发生变化。

答案 14 :(得分:4)

试试这个:

sed -i 's/subdomainA/subdomainB/g' `grep -ril 'subdomainA' *`

答案 15 :(得分:4)

您可以使用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
希望这会对你有帮助!!!

答案 16 :(得分:3)

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

答案 17 :(得分:2)

有点旧学但这适用于OS X.

几乎没有技巧:

•仅编辑当前目录

下扩展名为.sls的文件

•必须对.进行转义,以确保sed不会将其评估为“任何字符”

,用作sed分隔符,而不是通常的/

另请注意,这是编辑一个Jinja模板,以便在variable的路径中传递import(但这不是主题)。

首先,验证您的sed命令是否符合要求(这只会将更改打印到stdout,它不会更改文件):

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在此评论中所述页)。

快乐的人们!

答案 18 :(得分:2)

这里的版本应该比大多数版本更通用;例如,它不需要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'

答案 19 :(得分:2)

我只是使用上衣:

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>)" 

答案 20 :(得分:2)

According to this blog post:

find . -type f | xargs perl -pi -e 's/oldtext/newtext/g;'

答案 21 :(得分:2)

只是为了避免改变

  • NearlysubdomainA.example.com
  • subdomainA.example.comp.other

但仍然

  • subdomainA.example.com.IsIt.good

(可能在域根背后的想法不太好)

find /home/www/ -type f -exec sed -i 's/\bsubdomainA\.example\.com\b/\1subdomainB.example.com\2/g' {} \;

答案 22 :(得分:2)

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

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取代

答案 23 :(得分:2)

如果您不介意将vimgrepfind工具一起使用,则可以在此链接中跟进用户Gert给出的答案 - &gt; 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 | update

答案 24 :(得分:1)

如果您需要排除目录--exclude-dir=.svn)并且还可能具有带空格的文件名(将0Byte与{{1}一起使用) }和grep -Z

xargs -0

答案 25 :(得分:1)

要替换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)可以分隔文件名,即使它们包含空格或诸如此类的东西。< / p>

答案 26 :(得分:1)

更改多个文件(并将备份保存为*.bak):

perl -p -i -e "s/\|/x/g" *

将获取目录中的所有文件,并将|替换为x 被称为“Perl馅饼”(简单的馅饼)

答案 27 :(得分:1)

perl -p -i -e 's/oldthing/new_thingy/g' `grep -ril oldthing *`

答案 28 :(得分:1)

对于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

答案 29 :(得分:1)

使用grepsed

的组合
for pp in $(grep -Rl looking_for_string)
do
    sed -i 's/looking_for_string/something_other/g' "${pp}"
done

答案 30 :(得分:1)

如果你想在不完全破坏你的SVN存储库的情况下使用它,你可以通过这样做来告诉'find'忽略所有隐藏文件:

find . \( ! -regex '.*/\..*' \) -type f -print0 | xargs -0 sed -i 's/subdomainA.example.com/subdomainB.example.com/g'

答案 31 :(得分:0)

更简单的方法是在命令行上使用以下内容

find /home/www/ -type f|xargs perl -pi -e 's/subdomainA\.example\.com/subdomainB.example.com/g' 

答案 32 :(得分:0)

如果您有权访问节点,则可以执行npm install -g rexreplace然后

rexreplace 'subdomainA.example.com' 'subdomainB.example.com' /home/www/**/*.*

答案 33 :(得分:0)

将所有与 string_1 匹配的内容替换为当前所有 .c .h 文件的 string_2 目录和子目录(不包括.git /)

适用于 Mac

find . -type f -path "*.git*" -prune -o -name '*\.[ch]' -exec \
sed -i '' -e 's/'$1'/'$2'/g' {} +

这应该适用于 Linux (尚未测试):

find . -type f -path "*.git*" -prune -o -name '*\.[ch]' -exec \
sed -i 's/string_1/string_2/g' {} +

答案 34 :(得分:0)

这是我为OSX和Windows(msys2)找到的最佳解决方案。应该可以使用任何可以获得sed的gnu版本的东西。跳过.git目录,这样就不会破坏你的校验和。

在Mac上,只需先安装coreutils并确保gsed位于路径中 -

brew install coreutils

然后我将此功能粘贴在我的zshrc / bashrc - &gt;

replace-recursive() {
    hash gsed 2>/dev/null && local SED_CMD="gsed" || SED_CMD="sed"
    find . -type f -name "*.*" -not -path "*/.git/*" -print0 | xargs -0 $SED_CMD -i "s/$1/$2/g"
}

usage: replace-recursive <find> <replace>