我怎么能做一个递归查找/替换的字符串与awk或se?

如何查找并替换每次出现的:

subdomainA.example.com

subdomainB.example.com

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

700343 次浏览
cd /home/www && find . -type f -print0 |xargs -0 perl -i.bak -pe 's/subdomainA\.example\.com/subdomainB.example.com/g'
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

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”标志告诉查找对找到的每个文件运行以下命令。

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

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

每个find手册页:“命令行的构建方式与xargs构建它的命令行。”

因此,无需使用xargs -0-print0即可实现您的目标(并处理包含空格的文件名)。

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

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

与这里的其他答案相比,这比大多数答案都简单,并且使用了se而不是perl,这是最初的问题所要求的。

更简单的方法是在命令行上使用下面的命令

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

试试这个:

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

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

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个文件,而其他命令花费了很长时间,我无法等到它们完成。

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

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

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

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

#!/usr/local/bin/bash -x
find * /home/www -type f | while read filesdo
sedtest=$(sed -n '/^/,/$/p' "${files}" | sed -n '/subdomainA/p')
if [ "${sedtest}" ]thensed s'/subdomainA/subdomainB/'g "${files}" > "${files}".tmpmv "${files}".tmp "${files}"fi
done

所有的技巧几乎都是一样的,但我喜欢这个:

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

  • -type f

    文件类型:常规文件

  • -exec command {} +

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

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

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

对我来说最简单的方法是

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

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

qsh命令的限制:

  • 查找没有-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} ]; thentouch -C 819 ${TEMP_FILE}
sed -e 's/'$SEARCH'/'$REPLACE'/g' \< ${file} > ${TEMP_FILE}
mv ${TEMP_FILE} ${file}fidone

注意事项:

  • 解决方案不包括错误处理
  • 不是Bash标记的OP

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

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

使用grepsed的组合

for pp in $(grep -Rl looking_for_string)dosed -i 's/looking_for_string/something_other/g' "${pp}"done

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

交易是这样的:

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

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

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

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

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

我想大多数人都不知道他们可以将某些内容管道到“同时读取文件”中,它避免了那些讨厌的-print0参数,同时在文件名中预先设置空格。

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

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

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

brew install coreutils

然后我把这个函数放在我的zshrc/bashrc->

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>

这个与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/

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

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

希望对你有帮助!!!

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

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

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

有点老派,但这在OS X上有效。

有一些诡计:

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

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

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

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

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

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

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

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

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

乡亲们快乐!

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

这适用于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' {} +

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

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

对于替换git存储库中的所有出现,您可以使用:

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

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

只是为了避免改变

  • 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' {} \;
perl -p -i -e 's/oldthing/new_thingy/g' `grep -ril oldthing *`

要将文件递归地减少到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 | xargs perl -pi -e 's/oldtext/newtext/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,这只在计划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'

一个直接的方法,如果你需要排除目录--exclude-dir=..folder),也可能有带空格的文件名(通过对grep -Zxargs -0使用0Byte解决)

grep -rlZ oldtext . --exclude-dir=.folder | xargs -0 sed -i 's/oldtext/newtext/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

或者使用极速的GNU并行:

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

find(1)替换为更简单的fd(1)/fdfind=https://github.com/sharkdp/fd

fdfind . --type f --exec sed -i "s/original_string/new_string/g"

寻址fd(1)一致的pkg和cmd名称

  • 在macOS自制:pkg和cmd=fd
  • 在Ubuntu 20.04上:pkg=fd-find, cmd=fdfind

我在macOS上做了一个alias fdfind='fd',以实现一致的cmd命名(在我的macOS和Linux平台之间)。

更多关于这一点在https://github.com/sharkdp/fd/issues/1009

更多细节和附加功能

# bash examples:
1='original_string'2='new______string'
# for this (the original-poster's) question:1='subdomainA.example.com'2='subdomainB.example.com'
# 'fdfind' (on at least Ubuntu 20.04) = 'fd' = https://github.com/sharkdp/fd
fdfind . --type f --exec sed -i "s/$1/$2/g"
# Here's a slightly-more-complex example that# a. excludes (-E) .git/ and archive/ dirs, and# b. performs a word-boundary search on the original_string (\<$1\>):fdfind . -E .git/ -E archive/ --type f --exec sed -i "s/\<$1\>/$2/g"

更奇妙的是:从第三个($3)命令行参数控制单词边界(第三个参数=noword表示没有边界,leftword表示只有左侧单词边界,rightword表示只有右侧边界):

#!/usr/bin/env bash
## replace-tree.bash#
# 'fdfind' (on at least Ubuntu 20.04) = 'fd' = https://github.com/sharkdp/fd
if [ $# -lt 2 ]; thenecho "$0: Please provide at least 2 arguments."exit 1fi
original="\<$1\>"
if   [ "$3" = "noword" ];    thenoriginal="$1"elif [ "$3" = "leftword" ];  thenoriginal="\<$1"elif [ "$3" = "rightword" ]; thenoriginal="$1\>"fi
fdfind . --type f --exec sed -i "s/$original/$2/g"

示例用法:

$ replace-tree.bash original_string new_string leftword$

我很惊讶我还没有看到使用文件全局的简单答案,我只是用来扫描/更新只有packge.json文件**/package.json

这是zsh下的macos专用

cd /home/wwwsed -i '' -e 's/subdomainA.example.com/subdomainA.example.com/g' **/*