ChatGPT解决这个技术问题 Extra ChatGPT

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

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

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

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

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


I
Iulian Onofrei

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

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

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

如果您真的真的只想使用 bash,那么以下方法可以工作:

while IFS='' read -r a; do
    echo "${a//abc/XYZ}"
done < /tmp/file.txt > /tmp/file.txt.t
mv /tmp/file.txt{.t,}

这会遍历每一行,进行替换,然后写入临时文件(不想破坏输入)。最后的移动只是临时移动到原始名称。 (为了稳健性和安全性,临时文件名不应该是静态的或可预测的,但我们不要去那里。)

对于 Mac 用户:

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

(请参阅下面的评论为什么)


除了调用 mv 几乎与使用 sed 一样“非 Bash”。我几乎对 echo 说了同样的话,但它是一个内置的 shell。
但是,Solaris 不存在 sed 的 -i 参数(我认为还有其他一些实现),所以请记住这一点。只是花了几分钟才弄清楚...
自我注意:关于 sed 的正则表达式:s/..../..../ - Substitute/g - Global
遇到 invalid command code C 错误的 Mac 用户请注意... 对于就地替换,BSD sed 需要在 -i 标志之后添加文件扩展名,因为它保存具有给定扩展名的备份文件。例如:sed -i '.bak' 's/find/replace/' /file.txt 您可以使用空字符串跳过备份,如下所示:sed -i '' 's/find/replace/' /file.txt
提示:如果您想要不区分大小写的替换,请使用 s/abc/XYZ/gi
J
John Kugelman

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

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

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

有关更多详细信息,包括如何备份原始文件,请参阅 man perlrun


我内心的纯粹主义者说你不能确定 Perl 会在系统上可用。但如今这种情况很少见。也许我暴露了我的年龄。
你能举一个更复杂的例子吗?比如用“chdir /blah2”替换“chdir /blah”。我尝试了 perl -pi -e 's/chdir (?:\\/[\\w\\.\\-]+)+/chdir blah/g' text,但我一直收到错误,在 -e 第 1 行不推荐使用模式和后面的单词之间没有空格。不匹配的(在正则表达式中;在 m/(chdir)() 中由 <-- HERE 标记)( <-- 这里 ?:\\/ 在 -e 第 1 行。
@CMCDragonkai 检查这个答案:stackoverflow.com/a/12061491/2730528
t
the Tin Man

当我偶然发现这个时,我很惊讶...

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

# 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


这里有两件事可能:a) replace 是一个有用的独立工具,MySQL 人员应该单独发布它并依赖它 b) replace 需要一些 MySQL o_O 无论哪种方式,安装 mysql-server 以获取替换将做错事:)
只适用于mac?在我的 ubuntu 我 centos 中,该命令不存在
那是因为您没有安装 mysql-server 软件包。正如@rayro 所指出的,replace 是其中的一部分。
“警告:replace 已弃用,将在未来版本中删除。”
注意不要在 Windows 上运行 REPLACE 命令!在 Windows 上,REPLACE 命令用于快速复制文件。与本次讨论无关。
J
John Kugelman

这是一篇旧帖子,但对于任何想要使用变量的人来说,@centurian 说单引号意味着什么都不会被扩展。

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

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

我在一个文件中使用它:sed "s/\"$li\"/- [x]\"\${li:5}\"/" $dat ang get sed unterminated `s' command
问题解决了,嗯,... $li 来自文件行,所以有例如 \n 并且错误就在那里。所以要么 awk 要么像 python 这样的其他语言出现。
P
P i

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

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

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

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

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

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

示例类似于没有全屏模式的 vi。您可以在 vi: 提示符下给它同样的命令。


@awattar 在 for 循环中一次做一个。
是否可以选择将它与 globbing 一起用作一个衬里?
据我所知不是。 for f in report*.txt; do echo "%s/abc/XYZ/g \n w \n q \n" | ex file; done 简洁明了。为什么要将 shell 已有的功能放入 ex 中?
(或者,如果您的问题超出了 shell,请使用 Python/Perl/whatever)
t
the Tin Man

我发现了这个线程,我同意它包含最完整的答案,所以我也添加了我的:

sed 和 ed 非常有用……手动。从@Johnny 看这段代码: sed -i -e 's/abc/XYZ/g' /tmp/file.txt 当我的限制是在 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 所说,使用一段时间阅读......但不幸的是,这并不是故事的结局。以下对我很有效: #edit user's virtual domain result= #if nullglob is set then, unset it temporary is_nullglob=$( shopt -s | egrep -i '*nullglob' ) if [[ is_nullglob ]];然后 shopt -u nullglob fi while IFS= read -r line; do line="${line//''/$server}" line="${line//''/$alias}" line="${line//'' /$user}" line="${line//''/$group}" result="$result""$line"'\n' done < $tmp echo -e $result > $tmp #如果设置了 nullglob,则重新启用它 if [[ is_nullglob ]];然后 shopt -s nullglob fi #将用户的虚拟域移动到 Apache 2 域目录......正如我们可以看到的那样,如果 nullglob 设置了,当有一个包含 * 的字符串时,它的行为很奇怪,如: ServerName www.example.com 变成

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


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

您可以使用 sed

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

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

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

在所有 Python 文件中查找和替换:

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

-i 不是“忽略大小写”,而是 -i[SUFFIX], --in-place[=SUFFIX](就地编辑文件(如果提供了 SUFFIX,则进行备份))
L
Linux4you

如果用“/”字符替换 URL,请小心。

如何做到这一点的一个例子:

sed -i "s%http://domain.com%http://www.domain.com/folder/%g" "test.txt"

摘自:http://www.sysadmit.com/2015/07/linux-reemplazar-texto-en-archivos-con-sed.html


j
johnraff

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

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

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

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


将其与制表符一起使用的任何提示?出于某种原因,我的脚本在从带有大量转义的 sed 更改为此方法后,找不到任何带有选项卡的内容。
如果您想在要替换的字符串中放置一个制表符,可以使用 Bash 的“美元单引号”语法,因此制表符由 $'\t' 表示,您可以使用 $ echo 'tab'$ '\t''分离' > 测试文件; $ file_contents=$(
t
the Tin Man

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

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

在“Editing files via scripts with ed”中查看更多信息。


此解决方案独立于 GNU/FreeBSD (Mac OSX) 的不兼容性(与 sed -i <pattern> <filename> 不同)。非常好!
k
kenorb

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

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

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

这相当于ex编辑器的@slim answer,基本上是一样的。

以下是几个ex实际示例。

将文件中的文本 foo 替换为 bar

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

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

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

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

添加 -V1 参数以显示详细消息。

强制退出:-cwq!。

也可以看看:

如何以非交互方式编辑文件(例如在管道中)?在 Vi SE


想要以交互方式进行替换。因此尝试了“vim -esnc '%s/foo/bar/gc|:wq' file.txt”。但是终端现在卡住了。我们如何在没有 bash shell 行为怪异的情况下以交互方式进行替换。
要调试,请添加 -V1,要强制退出,请使用 wq!
k
kenorb

尝试以下 shell 命令:

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

这是对“我如何不小心将所有子目录中的所有文件也”的一个很好的答案,但这似乎不是这里要问的。
此语法不适用于 BSD 版本的 sed,请改用 sed -i''
m
micalith

您也可以在 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()

S
Shivam Tyagi

使用 sed 命令替换文件中多个文本的最简单方法

命令 -

sed -i 's#a/b/c#D/E#g;s#/x/y/z#D:/X#g;'文件名

在上面的命令 s#a/b/c#D/E#g 中,我将 a/b/c 替换为 D/E,然后在 ;我们再次做同样的事情


z
zalex

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

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

这不是明确的原因,但它非常快速且有用。 :)


S
Szekelygobe

对于 MAC 用户,以防您不阅读评论 :)

正如@Austin 所述,如果您收到 Invalid command code 错误

对于就地替换,BSD sed 需要在 -i 标志之后有一个文件扩展名,以保存到具有给定扩展名的备份文件中。

sed -i '.bak' 's/find/replace' /file.txt

如果要跳过备份,可以使用 '' 空字符串。

sed -i '' 's/find/replace' /file.txt

@Austin 的所有优点


S
SMRITI MAHESHWARI

如果一起对多个文件进行更改,我们可以在一行中执行以下操作:-

user_name='whoami'
for file in file1.txt file2.txt file3.txt; do sed -i -e 's/default_user/${user_name}/g' $file; done

添加如果以防万一可能有用。