awk / sed:如何做一个string的recursion查找/replace?
如何查找和replace以下每个事件:
subdomainA.example.com
同
subdomainB.example.com
在/home/www/
目录树(recursion查找/replace)下的每个文本文件中。
注意 :不要在包含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 only)告诉find使用空字符(\ 0)而不是空格作为find的path名之间的输出分隔符。 如果您的文件可以包含空格或其他特殊字符,这是一个更安全的选项。 build议使用-print0参数来查找是否使用-exec命令或xargs(在xargs中需要-0参数)。
注意 :不要在包含git repo的文件夹上运行这个命令 – 修改.git可能会破坏你的git索引。
find /home/www/ -type f -exec \ sed -i 's/subdomainA\.example\.com/subdomainB.example.com/g' {} +
与其他答案相比,这比大多数简单,并使用sed,而不是perl,这是原来的问题要求。
所有的技巧都差不多,但我喜欢这个:
find <mydir> -type f -exec sed -i 's/<string1>/<string2>/g' {} +
-
find <mydir>
:在目录中查找。 -
-type f
:文件是types:常规文件
-
-exec command {} +
:-exec操作的这种变体在选定的文件上运行指定的命令,但命令行是通过在每个选定的文件名后加上来构build的; 该命令的总调用次数将远远less于匹配文件的数量。 命令行的构build方式与xargs构build其命令行的方式大致相同。 命令中只允许有一个“{}”实例。 该命令在起始目录中执行。
cd /home/www && find . -type f -print0 | xargs -0 perl -i.bak -pe 's/subdomainA\.example\.com/subdomainB.example.com/g'
对我来说最简单的方法是
grep -rl oldtext . | xargs sed -i 's/oldtext/newtext/g'
对我来说,最简单的解决scheme是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
解决scheme。
对于任何使用silverlightsearch者 ( ag
)
ag SearchString -l0 | xargs -0 sed -i 's/SearchString/Replacement/g'
由于默认情况下,g会忽略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"
这个与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/ )
我只是需要这个,并不满意现有例子的速度。 所以我想出了我自己的:
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在查找相关文件方面非常高效。 这个命令轻而易举地取代了大约145000个文件,而其他的则花了这么长时间,我不能等到它们完成。
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在find的每个文件上运行以下命令。
perl -i.bak -pe 's/subdomainA\.example\.com/subdomainB.example.com/g' {} +
是在文件上运行的命令(一次很多)。 {}
被文件名取代。 命令末尾的+
告诉find
为许多文件名构build一个命令。
根据find
手册页:“命令行的构build方式与xargs构build命令行的方式大致相同。
因此,可以在不使用xargs -0
或-print0
的情况下实现目标(并处理包含空格的文件名)。
为了减less文件recursionsed
通过,你可以grep
你的string实例:
grep -rl <oldstring> /path/to/folder | xargs sed -is^<oldstring>^<newstring>^g
如果你运行man grep
你会注意到你也可以定义一个--exlude-dir="*.git"
标志,如果你想省略通过.git目录search,避免git索引问题,正如别人礼貌指出的那样。
引导您:
grep -rl --exclude-dir="*.git" <oldstring> /path/to/folder | xargs sed -is^<oldstring>^<newstring>^g
尝试这个:
sed -i 's/subdomainA/subdomainB/g' `grep -ril 'subdomainA' *`
#!/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
grep -lr 'subdomainA.example.com' | while read file; do sed -i "s/subdomainA.example.com/subdomainB.example.com/g" "$file"; done
我想大多数人不知道,他们可以pipe一些东西到“读取文件”,它避免了那些讨厌的-print0参数,而presevering空间的文件名。
在sed之前进一步添加echo
,可以让您在实际执行操作之前查看要更改的文件。
如果你不介意把vim
与grep
一起使用或者find
工具,那么你可以在这个链接中跟踪由用户Gert给出的答案 – > 如何在大文件夹层次结构中进行文本replace? 。
这是交易:
-
recursion地grep你想要在特定path中replace的string,并且只取得匹配文件的完整path。 (这将是
$(grep 'string' 'pathname' -Rl)
。 -
(可选)如果你想在集中式目录下做这些文件的预备份,也许你可以使用这个:
cp -iv $(grep 'string' 'pathname' -Rl) 'centralized-directory-pathname'
-
之后,你可以在
vim
按照类似于给出的链接提供的scheme编辑/replace:-
:bufdo %s#string#replacement#gc | update
-
对于IBMi上的Qshell(qsh),不是由OP标记的bash。
qsh命令的局限性:
- 找不到-print0选项
- xargs没有-0选项
- sed没有-i选项
因此qsh中的解决scheme:
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
注意事项:
- 解决scheme不包括error handling
- 不是由OP标记的Bash
如果你想在不完全破坏你的SVN仓库的情况下使用它,你可以通过下面的方法告诉'find'忽略所有隐藏的文件:
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
你可以使用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
希望这会帮助你!
有点古老,但这在OS X上工作。
有几个诡计:
•只能在当前目录下编辑扩展名为.sls
的文件
• .
必须逃脱,以确保sed
不评价他们为“任何性格”
•用作sed
分隔符而不是常用的/
还要注意,这是编辑一个Jinja模板来传递一个variable
在一个import
的path(但这是closures主题)。
首先,validation你的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 ''
,我不想创build原始文件的备份(正如在OS X上就地编辑sed或Robert Lujo在此页面中的注释中所述)。
快乐的人们!
只是为了避免改变
- 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' {} \;
更简单的方法是在命令行中使用下面的内容
find /home/www/ -type f|xargs perl -pi -e 's/subdomainA\.example\.com/subdomainB.example.com/g'
这是我发现的OSX和Windows(msys2)最好的解决scheme。 应该使用任何可以获得sed的GNU版本的东西。 跳过.git目录,这样不会破坏校验和。
在mac上,首先安装coreutils并确保gsed在path中 –
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>
将所有匹配string_1的内容replace为当前目录和子目录(不包括.git /)中的所有.c和.h文件的string_2 。
这适用于Mac :
find . -type f -path "*.git*" -prune -o -name '*\.[ch]' -exec \ sed -i '' -e 's/'$1'/'$2'/g' {} +
这应该在Linux上工作(还没有testing过):
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/**/*.*
为了replacegit仓库中的所有事件,你可以使用:
git ls-files -z | xargs -0 sed -i 's/subdomainA\.example\.com/subdomainB.example.com/g'
请参阅在本地git回购列表文件? 其他选项列出存储库中的所有文件。 -z
选项告诉git使用零字节来分隔文件名,这可以确保xargs
(带有选项-0
)可以分隔文件名,即使它们包含空格或不包含任何内容。
更改多个文件(并将备份保存为* .bak):
perl -p -i -e "s/\|/x/g" *
将所有目录下的文件replace为“|”,并用x称为“Perl pie”(很容易)
perl -p -i -e 's/oldthing/new_thingy/g' `grep -ril oldthing *`