如何在linux命令行中替换多个文件中的字符串

时间:2012-07-09 09:37:48

标签: linux string

我需要替换文件夹中许多文件中的字符串,只有ssh访问服务器的权限。我怎么能这样做?

28 个答案:

答案 0 :(得分:476)

cd /path/to/your/folder
sed -i 's/foo/bar/g' *

“foo”的出现将被替换为“bar”。

答案 1 :(得分:195)

类似于Kaspar的答案,但使用g标志来替换一行中的所有事件。

find ./ -type f -exec sed -i 's/string1/string2/g' {} \;

对于全局不区分大小写:

find ./ -type f -exec sed -i 's/string1/string2/gI' {} \;

答案 2 :(得分:120)

@kev的答案很好,但只影响直接目录中的文件。下面的例子使用grep来递归查找文件。它每次都适合我。

grep -rli 'old-word' * | xargs -i@ sed -i 's/old-word/new-word/g' @

答案 3 :(得分:28)

这对我有用:

find ./ -type f -exec sed -i 's/string1/string2/' {} \;

但是,这不是:sed -i 's/string1/string2/g' *。也许“foo”不是string1而“bar”不是string2。

答案 4 :(得分:25)

已经列出了一些标准答案。通常,您可以使用查找递归列出文件,然后使用 sed perl 执行操作。

对于大多数快速用途,您可能会发现命令 rpl 更容易记住。这是替换(foo - > bar),递归地对所有文件:

rpl -R foo bar .

您可能需要安装它(apt-get install rpl或类似的)。

然而,对于涉及正则表达式和反向替换,或文件重命名以及搜索和替换的更艰难的工作,我所知道的最通用和最强大的工具是repren,一个小的Python脚本我曾经写过一些棘手的重命名和重构任务。您可能更喜欢它的原因是:

  • 支持重命名文件以及搜索和替换文件内容。
  • 在您提交执行搜索和替换之前查看更改。
  • 支持带有后置替换,整个单词,不区分大小写和保留大小写的正则表达式(替换foo - > bar,Foo - > Bar,FOO - > BAR)模式。
  • 使用多个替换项,包括交换(foo - > bar和bar - > foo)或非唯一替换项集(foo - > bar,f - > x)。

要使用它,pip install reprenCheck the README例如。

答案 5 :(得分:24)

要替换多个文件中的字符串,您可以使用:

grep -rl string1 somedir/ | xargs sed -i 's/string1/string2/g'

E.g。

grep -rl 'windows' ./ | xargs sed -i 's/windows/linux/g'

Source blog

答案 6 :(得分:17)

要替换文件中的路径(避免使用转义字符),您可以使用以下命令:

sed -i 's@old_path@new_path@g'

@符号表示应在后面的字符串中忽略所有特殊字符。

答案 7 :(得分:14)

如果您的字符串中包含正斜杠(/),您可以将分隔符更改为' +'。

find . -type f -exec sed -i 's+http://example.com+https://example.com+g' {} +

此命令将在当前目录中递归运行。

答案 8 :(得分:11)

“foo”的第一行出现将替换为“bar”。你可以使用第二行来检查。

grep -rl 'foo' . | xargs sed -i 's/foo/bar/g'
grep 'foo' -r * | awk -F: {'print $1'} | sort -n | uniq -c

答案 9 :(得分:8)

如果您有可以使用的文件列表

replace "old_string" "new_string" -- file_name1 file_name2 file_name3

如果您拥有所有可以使用的文件

replace "old_string" "new_string" -- *

如果您有包含扩展名的文件列表,则可以使用

replace "old_string" "new_string" -- *.extension

答案 10 :(得分:4)

在找到这个问题(和答案)之前,我确实编造了我自己的解决方案。 我搜索了"替换"的不同组合。 "几个"和" xml,"因为那是我的申请,但没有找到这个特别的。

我的问题:我有弹簧xml文件,其中包含测试用例的数据,包含复杂对象。 java源代码的重构改变了很多类,并没有应用于xml数据文件。为了保存测试用例数据,我需要更改分布在多个目录中的所有xml文件中的所有类名。同时保存原始xml文件的备份副本(虽然这不是必须的,因为版本控制会在这里保存我。)

我正在寻找find + sed的某种组合,因为它在其他情况下对我有用,但不能同时进行多次替换。

然后我找到了ask ubuntu response,它帮助我构建了命令行:

find -name "*.xml" -exec sed -s --in-place=.bak -e 's/firstWord/newFirstWord/g;s/secondWord/newSecondWord/g;s/thirdWord/newThirdWord/g' {} \;

它完美运作(好吧,我的案例有六种不同的替代品)。但请注意,它将触及当前目录下的所有* .xml文件。因此,如果您对版本控制系统负责,您可能希望先过滤,然后只传递给sed那些实际拥有您想要的字符串的人;像:

find -name "*.xml" -exec grep -e "firstWord" -e "secondWord" -e "thirdWord" {} \; -exec sed -s --in-place=.bak -e 's/firstWord/newFirstWord/g;s/secondWord/newSecondWord/g;s/thirdWord/newThirdWord/g' {} \;

答案 11 :(得分:4)

"你也可以使用find和sed,但我发现perl的这一小行很好用。

perl -pi -w -e 's/search/replace/g;' *.php
  • -e表示执行以下代码行。
  • -i表示就地编辑
  • -w写警告
  • -p loop

" (摘自http://www.liamdelahunty.com/tips/linux_search_and_replace_multiple_files.php

我最好的结果来自于使用perl和grep(以确保文件具有搜索表达式)

perl -pi -w -e 's/search/replace/g;' $( grep -rl 'search' )

答案 12 :(得分:3)

grep --include={*.php,*.html} -rnl './' -e "old" | xargs -i@ sed -i 's/old/new/g' @

答案 13 :(得分:2)

用于multiedit命令的

脚本

multiedit [-n PATTERN] OLDSTRING NEWSTRING

根据Kaspar的回答,我创建了一个bash脚本来接受命令行参数,并可选择限制与模式匹配的文件名。保存在$ PATH中并生成可执行文件,然后只需使用上面的命令。

这是脚本:

#!/bin/bash
_help="\n
Replace OLDSTRING with NEWSTRING recursively starting from current directory\n
multiedit [-n PATTERN] OLDSTRING NEWSTRING\n

[-n PATTERN] option limits to filenames matching PATTERN\n
Note: backslash escape special characters\n
Note: enclose STRINGS with spaces in double quotes\n
Example to limit the edit to python files:\n
multiedit -n \*.py \"OLD STRING\" NEWSTRING\n"

# ensure correct number of arguments, otherwise display help...
if [ $# -lt 2 ] || [ $# -gt 4 ]; then echo -e $_help ; exit ; fi
if [ $1 == "-n" ]; then  # if -n option is given:
        # replace OLDSTRING with NEWSTRING recursively in files matching PATTERN
        find ./ -type f -name "$2" -exec sed -i "s/$3/$4/g" {} \;
else
        # replace OLDSTRING with NEWSTRING recursively in all files
        find ./ -type f -exec sed -i "s/$1/$2/" {} \;
fi

答案 14 :(得分:2)

真的很蹩脚,但我无法让任何sed命令在OSX上正常工作,所以我做了这个愚蠢的事情:

:%s/foo/bar/g
:wn

^ - 将这三行复制到我的剪贴板中(是的,包括结尾换行符),然后:

vi *

并按住command-v,直到它说没有文件为止。

...哑哈克...有效...

答案 15 :(得分:1)

如果文件包含反斜杠(通常是路径),您可以尝试这样的事情:

sed -i -- 's,<path1>,<path2>,g' *

例如:

sed -i -- 's,/foo/bar,/new/foo/bar,g' *.sh (in all shell scripts available)

答案 16 :(得分:1)

为了维护我的个人英语节点,我编写了一个实用程序脚本,帮助为递归目录下的所有文件替换多对旧/新字符串。

在哈希映射中管理多对旧/新字符串。

可以通过命令行或环境变量设置目录,地图在脚本中进行硬编码,但如果需要,您可以修改要从文件加载的代码。

由于一些新功能,它需要bash 4.2。

<强> en_standardize.sh:

#! /bin/bash
# (need bash 4.2+,)
# 
# Standardize phonetic symbol of English.
# 
# format:
#   en_standardize.sh [<dir>]
# 
# params:
# * dir
#   target dir, optional,
#   if not specified then use environment variable "$node_dir_en",
#   if both not provided, then will not execute,
# * 
# 

paramCount=$#

# figure target dir,
if [ $paramCount -ge 1 ]; then # dir specified
    echo -e "dir specified (in command):\n\t$1\n"
    targetDir=$1
elif [[ -v node_dir_en ]]; then # environable set,
    echo -e "dir specified (in environment vairable):\n\t$node_dir_en\n"
    targetDir=$node_dir_en
else # environable not set,
    echo "dir not specified, won't execute"
    exit
fi

# check whether dir exists,
if [ -d $targetDir ]; then
    cd $targetDir
else
    echo -e "invalid dir location:\n\t$targetDir\n"
    exit
fi

# initial map,
declare -A itemMap
itemMap=( ["ɪ"]="i" ["ː"]=":" ["ɜ"]="ə" ["ɒ"]="ɔ" ["ʊ"]="u" ["ɛ"]="e")

# print item maps,
echo 'maps:'
for key in "${!itemMap[@]}"; do
    echo -e "\t$key\t->\t${itemMap[$key]}"
done
echo -e '\n'

# do replace,
for key in "${!itemMap[@]}"; do
    grep -rli "$key" * | xargs -i@ sed -i "s/$key/${itemMap[$key]}/g" @
done

echo -e "\nDone."
exit

答案 17 :(得分:1)

当使用-i开关调用时,流编辑器会“修复”多个文件“inplace”,这会将备份文件作为参数结束。所以

sed -i.bak 's/foo/bar/g' *

foo替换为此文件夹中所有文件中的bar,但不会下降到子文件夹中。但是,这将为目录中的每个文件生成一个新的.bak文件。 要以递归方式对此目录及其所有子目录中的所有文件执行此操作,您需要一个帮助程序(如find)来遍历目录树。

find ./ -print0 | xargs -0 sed -i.bak 's/foo/bar/g' *

find允许您通过在必要时指定find ./ -name '*.php' -or -name '*.html' -print0等其他参数来进一步限制要修改的文件。


注意:GNU sed不需要文件结尾,sed -i 's/foo/bar/g' *也可以使用; FreeBSD sed需要扩展名,但允许介于两者之间的空格,因此sed -i .bak s/foo/bar/g *可以正常工作。

答案 18 :(得分:1)

假设您要搜索字符串search,并在多个文件中将其替换为replace,则为my battle-tested, one-line formula

grep -RiIl 'search' | xargs sed -i 's/search/replace/g'

grep快速说明:

  • -R-递归搜索
  • -i-不区分大小写
  • -I-跳过二进制文件(想要文本,对吧?)
  • -l-打印一个简单列表作为输出。其他命令需要

然后,将grep输出通过管道传递到sed(通过xargs),用于实际替换文本。 -i标志将直接更改文件。删除它以实现“空转”模式。

答案 19 :(得分:1)

在MacBook Pro上,我使用了以下命令(受https://stackoverflow.com/a/19457213/6169225的启发):

sed -i '' -e 's/<STR_TO_REPLACE>/<REPLACEMENT_STR>/g' *
  

-i ''将确保您不备份。

     

-e(用于现代正则表达式)。

答案 20 :(得分:1)

像这样使用ack命令的速度会更快:

ack '25 Essex' -l | xargs sed -i 's/The\ fox \jump/abc 321/g'

如果搜索结果中有空格,则也是如此。您需要逃脱它。

答案 21 :(得分:0)

我对许多答案的问题是我需要替换许多文件中的文件路径。尽管提供的一个答案提到了这一点,但它对我不起作用。我的解决方案:

首先,生成您要更改的文件名列表。

filelist=($(find /path/to/your/folder | xargs grep '/path/to/fix' | cut -d : -f 1 | tr '\n' ' '))

上面的命令所做的是通过管道传输到 findgrep 生成包含 /path/to/fix 的文件的名称。但是,grep 还会打印出找到字符串所在的行,因此 cut 命令摆脱了这一点,只保留文件名。 tr 用空格替换换行符,允许将 filelist 存储为数组。

for file in "${filelist[@]}"; do sed -i.bak 's+/path/to/fix+/new/path/for/my/file+g' $file; done

这个 sed 命令借鉴了这个问题的其他答案,并使用 + 作为分隔符而不是普通的 /,因为 / 字符被用于文件路径。

答案 22 :(得分:0)

我只想添加一个注释以同时执行两项操作-使用find'chaining'方法查找包含字符串的文件,然后进行替换:

find  . -type f -iname \*.php -exec fgrep -l "www." {} \; -exec sed -i "s|www||g" {} \;      
  • 在这种情况下,请从PHP文件中的网址中删除过时的'www'。

  • 'fgrep -l'仅在文件中找到至少一个匹配项时触发,它不产生其他输出。不要忘记“ \;”分隔符!

答案 23 :(得分:0)

使用简单的脚本文件有一种更简单的方法:

   # sudo chmod +x /bin/replace_string_files_present_dir

在gedit或您选择的编辑器中打开文件,我在这里使用gedit。

   # sudo gedit /bin/replace_string_files_present_dir

然后在编辑器中将以下内容粘贴到文件中

   #!/bin/bash
   replace "oldstring" "newstring" -- *
   replace "oldstring1" "newstring2" -- *
   #add as many lines of replace as there are your strings to be replaced for 
   #example here i have two sets of strings to replace which are oldstring and 
   #oldstring1 so I use two replace lines.

保存文件,关闭 gedit ,然后退出终端或关闭它,然后启动它以加载您添加的新脚本。

导航到您要编辑多个文件的目录。然后运行:

  #replace_string_files_present_dir

按Enter键,这将自动用正确的 newstring 替换包含它们的所有文件中的 oldstring oldstring1 分别是newstring1

它将跳过所有不包含旧字符串的目录和文件。

如果您有多个目录包含需要替换字符串的文件,这可能有助于消除繁琐的键入工作。您要做的就是导航到每个目录,然后运行:

#replace_string_files_present_dir

您所要做的就是确保您已包含或添加了上面显示的所有替换字符串:

replace "oldstring" "newstring" -- *

位于文件 / bin / replace_string_files_present_dir 的末尾。

要添加新的替换字符串,只需在终端中键入以下内容来打开我们创建的脚本:

sudo gedit /bin/replace_string_files_present_dir

不用担心您添加的替换字符串的数量,如果找不到 oldstring ,它们将无效。

答案 24 :(得分:0)

我从另一个帖子中找到了这个(不记得哪个),虽然不是最优雅,但它很简单,作为新手,Linux用户给了我一点麻烦

for i in *old_str* ; do mv -v "$i" "${i/\old_str/new_str}" ; done

如果您有空格或其他特殊字符,请使用\

for i in *old_str\ * ; do mv -v "$i" "${i/\old_str\ /new_str}" ; done

对于子目录中的字符串使用**

for i in *\*old_str\ * ; do mv -v "$i" "${i/\old_str\ /new_str}" ; done

答案 25 :(得分:0)

我举一个例子来解决python源代码中常见的shebang错误。

您可以尝试使用grep / sed方法。这是一个适用于GNU sed并且不会破坏git repo的文件:

$ grep -rli --exclude '*.git*' '#!/usr/bin/python' . | xargs -I {} \
gsed -i '' -e 's/#!\/usr\/bin\/python/#!\/usr\/bin\/env python/' {}

或者您可以使用greptile :)

$ greptile -x .py -l -i -g '#!/usr/bin/env python' -r '#!/usr/bin/python' .

我刚刚测试了第一个脚本,第二个脚本也可以正常运行。小心转义字符,我认为在大多数情况下使用greptile会更容易。当然,你可以使用sed做很多有趣的事情,为此可能最好使用它与xargs一起使用。

答案 26 :(得分:0)

下面的命令可用于首先搜索文件并替换文件:

find . | xargs grep 'search string' | sed 's/search string/new string/g'

例如

find . | xargs grep abc | sed 's/abc/xyz/g'

答案 27 :(得分:-4)

$ replace“From”“To” - `find / path / to / folder -type f`

(替换 - MySQL数据库系统的字符串替换实用程序)