通过Bash命令在文本文件中查找和替换


561

查找和替换给定输入字符串(例如abc)并替换为另一个字符串(例如XYZ在file中)的最简单方法是什么/tmp/file.txt

我正在编写一个应用程序,并使用IronPython通过SSH执行命令-但我不太了解Unix,也不知道要寻找什么。

我听说Bash除了是命令行界面之外,还可以是一种非常强大的脚本语言。因此,如果这是真的,我假设您可以执行类似的操作。

我可以用bash做到吗,实现我的目标最简单的脚本(一行)是什么?

Answers:


927

最简单的方法是使用sed(或perl):

sed -i -e 's/abc/XYZ/g' /tmp/file.txt

由于该-i选项,它将调用sed进行就地编辑。可以从bash调用它。

如果您真的只想使用bash,则可以使用以下方法:

while read a; do
    echo ${a//abc/XYZ}
done < /tmp/file.txt > /tmp/file.txt.t
mv /tmp/file.txt{.t,}

这会遍历每一行,进行替换,然后写入一个临时文件(不想破坏输入内容)。最后的举动只是暂时地移动到原始名称。


3
除了调用mv与使用sed几乎一样,它是“ non Bash”。我几乎说了相同的回声,但这是内置的shell。
苗条

5
sed的-i参数在Solaris中不存在(我会认为其他实现),因此请记住这一点。只是花了几分钟才弄清楚了……
Panky

2
自我提醒:约的正则表达式seds/..../..../ - Substitute/g - Global
校验

89
对于出现invalid command code C错误的Mac用户请注意...对于就地替换,BSD sed-i标志后需要文件扩展名,因为它会保存具有给定扩展名的备份文件。例如:sed -i '.bak' 's/find/replace/' /file.txt 您可以通过使用空字符串来跳过备份,如下所示:sed -i '' 's/find/replace/' /file.txt
Austin

8
提示:如果要区分大小写,请使用s/abc/XYZ/gi
Boris D. Teoharov,2014年

166

文件操纵通常不是由Bash完成,而是由Bash调用的程序来完成,例如:

perl -pi -e 's/abc/XYZ/g' /tmp/file.txt

-i标志告诉它执行就地替换。

请参阅参考资料,man perlrun以获取更多详细信息,包括如何备份原始文件。


37
我的纯粹主义者说,您不能确定Perl在系统上是否可用。但是,如今这种情况很少见。也许我正在显示自己的年龄。
苗条

3
您能否显示一个更复杂的示例。类似于将“ chdir / blah”替换为“ chdir / blah2”。我试过了perl -pi -e 's/chdir (?:\\/[\\w\\.\\-]+)+/chdir blah/g' text,但是在-e第1行中不赞成使用模式和后续单词之间没有空格的情况下,我始终会收到错误消息。 ?:\\ /在-e行1
CMCDragonkai


69

当我偶然发现这一点时,我感到很惊讶。

软件包replace附带有一个命令"mysql-server",因此如果已安装,请尝试一下:

# replace string abc to XYZ in files
replace "abc" "XYZ" -- file.txt file2.txt file3.txt

# or pipe an echo to replace
echo "abcdef" |replace "abc" "XYZ"

看到 man replace更多信息,。


12
这里可能有两件事:a)replace是有用的独立工具,MySQL人员应单独发布并依赖它b)replace需要一些MySQL o_O两种方式,安装mysql-server以获得替换将是错误的事情:)
菲利普·怀特豪斯

仅适用于Mac?在我的ubuntu中,我不知道该命令是否存在
Paul

1
那是因为您没有安装mysql-server软件包。正如@rayro所指出的,replace它是其中的一部分。
Phius

2
“警告:不建议使用replace,并且在以后的版本中将其删除。”
史蒂芬·瓦雄

1
注意不要在Windows上运行REPLACE命令!在Windows上,REPLACE命令用于文件的快速复制。与本讨论无关。
Maor

52

这是一则古老的文章,但是对于任何想要使用变量的人,如@centurian所说,单引号表示不会扩展任何内容。

一种简单的获取变量的方法是进行字符串连接,因为这是通过在bash中并置来完成的,因此应该可以进行以下操作:

sed -i -e "s/$var1/$var2/g" /tmp/file.txt

39

与其他shell一样,Bash只是用于协调其他命令的工具。通常,您会尝试使用标准的UNIX命令,但是您当然可以使用Bash调用任何东西,包括您自己的编译程序,其他Shell脚本,Python和Perl脚本等。

在这种情况下,有两种方法可以做到这一点。

如果要读取文件,然后将其写入另一个文件,同时进行搜索/替换,请使用sed:

sed 's/abc/XYZ/g' <infile >outfile

如果您想就地编辑文件(就像在编辑器中打开文件,对其进行编辑然后保存一样),请向行编辑器“ ex”提供说明

echo "%s/abc/XYZ/g
w
q
" | ex file

Ex就像没有全屏模式的vi。您可以像在vi的':'提示符下输入相同的命令。


33

我在其他线程中找到了这个线程,我同意它包含了最完整的答案,所以我也添加了我的线程:

  1. sed而且ed非常有用。从@Johnny看下面的代码:

    sed -i -e 's/abc/XYZ/g' /tmp/file.txt
  2. 当我的限制是在shell脚本中使用它时,不能在其中使用任何变量来代替“ abc”或“ XYZ”。该BashFAQ似乎与我至少明白同意。因此,我不能使用:

    x='abc'
    y='XYZ'
    sed -i -e 's/$x/$y/g' /tmp/file.txt
    #or,
    sed -i -e "s/$x/$y/g" /tmp/file.txt

    但是,我们该怎么办?因为,@ Johnny说使用了一个,while read...但不幸的是,这还不是故事的结局。以下与我很好地工作:

    #edit user's virtual domain
    result=
    #if nullglob is set then, unset it temporarily
    is_nullglob=$( shopt -s | egrep -i '*nullglob' )
    if [[ is_nullglob ]]; then
       shopt -u nullglob
    fi
    while IFS= read -r line; do
       line="${line//'<servername>'/$server}"
       line="${line//'<serveralias>'/$alias}"
       line="${line//'<user>'/$user}"
       line="${line//'<group>'/$group}"
       result="$result""$line"'\n'
    done < $tmp
    echo -e $result > $tmp
    #if nullglob was set then, re-enable it
    if [[ is_nullglob ]]; then
       shopt -s nullglob
    fi
    #move user's virtual domain to Apache 2 domain directory
    ......
  3. 可以看到是否nullglob设置了then,当包含a的字符串包含*in 时,它的行为很奇怪:

    <VirtualHost *:80>
     ServerName www.example.com

    变成

    <VirtualHost ServerName www.example.com

    没有结束尖括号,Apache2甚至无法加载。

  4. 这种解析应该比一键搜索和替换要慢,但是,正如您已经看到的,对于四个不同的搜索模式,有四个变量在一个解析周期内起作用。

在给定的问题假设下,我能想到的最合适的解决方案。


12
在您的(2)中-您可以做到sed -e "s/$x/$y/",它将起作用。不是双引号。如果变量中的字符串本身包含具有特殊含义的字符,可能会造成严重的混乱。例如,如果x =“ /”或x =“ \”。遇到这些问题时,这可能意味着您应该停止尝试使用Shell来完成这项工作。
2013年

苗条的嗨,我发现您也反对使用Perl。您有什么解决方案?因为实际上我想动态更改文件中的路径,这意味着我在字符串中有很多/!
Mahdi 2013年

20

您可以使用sed

sed -i 's/abc/XYZ/gi' /tmp/file.txt

使用-i了“忽略大小写”如果你不知道要查找的文本是abcABCAbC

您可以使用findsed如果您不知道文件名:

find ./ -type f -exec sed -i 's/abc/XYZ/gi' {} \;

查找并替换所有Python文件:

find ./ -iname "*.py" -type f -exec sed -i 's/abc/XYZ/gi' {} \;

12

您也可以使用该ed命令进行文件内搜索和替换:

# delete all lines matching foobar 
ed -s test.txt <<< $'g/foobar/d\nw' 

请参见“ 使用脚本编辑文件ed ”中的更多内容。


3
该解决方案独立于GNU / FreeBSD(Mac OSX)的不兼容性(不同于sed -i <pattern> <filename>)。非常好!
Peterino 2014年

11

如果您正在处理的文件不是很大,并且暂时将其存储在变量中没问题,那么您可以一次在整个文件上使用Bash字符串替换-无需逐行进行遍历:

file_contents=$(</tmp/file.txt)
echo "${file_contents//abc/XYZ}" > /tmp/file.txt

整个文件的内容将被视为一个长字符串,包括换行符。

XYZ可以是一个变量,例如$replacement,这里不使用sed的一个优点是您不必担心搜索或替换字符串可能包含sed模式定界符(通常但不一定是/)。缺点是无法使用正则表达式或sed的任何更复杂的操作。


有关将其与制表符一起使用的任何提示?出于某种原因,从具有大量转义的sed更改为此方法后,我的脚本在制表符上找不到任何内容。
Brian Hannay

2
如果要在要替换的字符串中放置一个制表符,则可以使用Bash的“美元单引号”语法,因此制表符由$'\ t'表示,并且可以执行$ echo'tab'$ '\ t''分隔的>测试文件; $ file_contents = $(<测试文件); $ echo“ $ {file_contents // $'\ t'/ TAB}”; tabTABseparated`
johnraff


5

尝试以下shell命令:

find ./  -type f -name "file*.txt" | xargs sed -i -e 's/abc/xyz/g'

4
这是对“我如何也偶然地所有子目录中的所有文件”的一个很好的答案,但这似乎不是这里要问的。
三人房

此语法不适用于的BSD版本sed,请sed -i''改用。
kenorb

5

要非交互地编辑文件中的文本,您需要就地文本编辑器,例如vim。

这是如何从命令行使用它的简单示例:

vim -esnc '%s/foo/bar/g|:wq' file.txt

这相当于@slim答案编辑器的基本上是同一回事。

这几个 ex实际的例子。

替换文字foobar文件中:

ex -s +%s/foo/bar/ge -cwq file.txt

删除多个文件的尾随空格:

ex +'bufdo!%s/\s\+$//e' -cxa *.txt

故障排除(端子卡住时):

  • -V1参数以显示详细消息。
  • 强制退出者:-cwq!

也可以看看:


希望以交互方式进行替换。因此尝试了“ vim -esnc'%s / foo / bar / gc |:wq'file.txt”。但是终端现在卡住了。我们将如何交互地进行替换,而bash shell不会出现奇怪的情况。
vineeshvs

要进行调试,请添加-V1,以强制退出,请使用wq!
kenorb

2

您也可以在bash脚本中使用python。我在这里的一些最佳答案并没有取得太大的成功,并且发现它不需要循环就可以工作:

#!/bin/bash
python
filetosearch = '/home/ubuntu/ip_table.txt'
texttoreplace = 'tcp443'
texttoinsert = 'udp1194'

s = open(filetosearch).read()
s = s.replace(texttoreplace, texttoinsert)
f = open(filetosearch, 'w')
f.write(s)
f.close()
quit()

1

您可以使用rpl命令。例如,您想在整个php项目中更改域名。

rpl -ivRpd -x'.php' 'old.domain.name' 'new.domain.name' ./path_to_your_project_folder/  

原因尚不清楚,但这是非常快速和有用的。:)

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.