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

How to do a recursive find/replace of a string with awk or sed?

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

1
subdomainA.example.com

1
subdomainB.example.com

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


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

来自man find

-print0 (GNU find only) tells find to use the null character (\0) instead of whitespace as the output delimiter between pathnames found. This is a safer option if your files can contain blanks or other special characters. It is recommended to use the -print0 argument to find if you use -exec or xargs (the -0 argument is needed in xargs.)


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

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

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


对我来说最简单的方法是

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


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

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

  • -type f

    File is of type: regular file

  • -exec command {} +

    This variant of the -exec action runs the specified command on the selected files, but the command line is built by appending
    each selected file name at the end; the total number of invocations of the command will be much less than the number of
    matched files. The command line is built in much the same way that xargs builds its command lines. Only one instance of
    `{}' is allowed within the command. The command is executed in the starting directory.


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


对我来说,最简单的解决方案是https://stackoverflow.com/a/2113224/565525,即:

1
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解决方案。


对于任何使用银色搜索器的人(ag)

1
ag SearchString -l0 | xargs -0 sed -i 's/SearchString/Replacement/g'

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


要将文件减少为递归sed,您可以grep为您的字符串实例:

1
grep -rl  /path/to/folder | xargs sed -i s^^<newstring>^g

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

引导您:

1
grep -rl --exclude-dir="*.git"  /path/to/folder | xargs sed -i s^^<newstring>^g

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

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


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

Linux的:

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

苹果电脑:

1
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/)


1
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在找到的每个文件上运行以下命令。

1
perl -i.bak -pe 's/subdomainA\.example\.com/subdomainB.example.com/g' {} +

是对文件运行的命令(一次多个)。 {}被文件名替换。
命令末尾的+告诉find为许多文件名构建一个命令。

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

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


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

1
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万个文件,而其他文件花费了很长时间,我不能等到它们完成。


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可以让您在实际执行之前查看哪些文件会发生变化。


您可以使用awk解决此问题,如下所示,

1
2
3
4
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

希望对你有帮助 !!!


试试这个:

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


1
2
3
4
5
6
7
8
9
10
11
12
13
14
#!/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

这是一个应该比大多数更通用的版本;例如,它不需要find(使用du代替)。它需要xargs,它只能在Plan 9的某些版本中找到(如9front)。

1
 du -a | awk -F' '  '{ print $2 }' | xargs sed -i -e 's/subdomainA\.example\.com/subdomainB.example.com/g'

如果要添加文件扩展名等过滤器,请使用grep

1
 du -a | grep"\.scala$" | awk -F' '  '{ print $2 }' | xargs sed -i -e 's/subdomainA\.example\.com/subdomainB.example.com/g'

我只是使用上衣:

1
2
find . -name '*.[c|cc|cp|cpp|m|mm|h]' -print0 |  xargs -0 tops -verbose  replace"verify_noerr(<b args>)" with"__Verify_noErr()" \
replace"check(<b args>)" with"__Check()"


根据这篇博文:

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


有点老派,但这适用于OS X.

几乎没有技巧:

?仅编辑当前目录下扩展名为.sls的文件

?必须转义.以确保sed不将它们评估为"任何字符"

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

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

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

1
2
for file in $(find . -name *.sls -type f); do echo -e"
$file:"
; sed 's,foo\.bar,foo/bar/"+baz+"/,g' $file; done

准备好进行更改后,根据需要编辑sed命令:

1
2
for file in $(find . -name *.sls -type f); do echo -e"
$file:"
; sed -i '' 's,foo\.bar,foo/bar/"+baz+"/,g' $file; done

请注意sed命令中的-i '',我不想创建原始文件的备份(如在OS X上使用sed进行就地编辑或在此页面中的Robert Lujo的注释中所述)。

快乐的人们!


只是为了避免改变

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

但仍然

  • subdomainA.example.com.IsIt.good

(也许在域根背后的想法不好)

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

如果您不介意将vimgrepfind工具一起使用,则可以在此链接中跟进用户Gert给出的答案 - >如何在大文件夹层次结构中进行文本替换?

这是交易:

  • 以递归方式grep查找要在特定路径中替换的字符串,并仅获取匹配文件的完整路径。 (那将是$(grep 'string' 'pathname' -Rl)

  • (可选)如果要在集中目录中对这些文件进行预备,也可以使用它:cp -iv $(grep 'string' 'pathname' -Rl) 'centralized-directory-pathname'

  • 之后,您可以按照类似于给定链接上提供的方案,在vim中随意编辑/替换:

    • :bufdo %s#string#replacement#gc | update

要替换git存储库中的所有匹配项,您可以使用:

1
git ls-files -z | xargs -0 sed -i 's/subdomainA\.example\.com/subdomainB.example.com/g'

请参阅本地git repo中的列表文件?用于列出存储库中所有文件的其他选项。 -z选项告诉git将文件名与零字节分开,这确保xargs(使用选项-0)可以分隔文件名,即使它们包含空格或诸如此类的东西。


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


使用grepsed的组合

1
2
3
4
for pp in $(grep -Rl looking_for_string)
do
    sed -i 's/looking_for_string/something_other/g'"${pp}"
done


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

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

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


对于IBMi上的Qshell(qsh),不是由OP标记的bash。

qsh命令的局限性:

  • find没有-print0选项
  • xargs没有-0选项
  • sed没有-i选项

因此qsh中的解决方案:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
    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


如果您想在不完全破坏SVN存储库的情况下使用它,可以通过执行以下操作告诉"find"忽略所有隐藏文件:

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


如果您需要排除目录(--exclude-dir=.svn)并且可能包含带空格的文件名(使用带有grep -Zxargs -0的0Byte)的直接方法

1
grep -rlZ oldtext . --exclude-dir=.svn | xargs -0 sed -i 's/oldtext/newtext/g'

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

这适用于Mac:

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

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

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

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

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

1
brew install coreutils

然后我把这个函数粘贴在我的zshrc / bashrc - >中

1
2
3
4
5
6
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>

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

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

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

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