关于ssh:从Bash命令查找和替换文本文件

Find and Replace Inside a Text File from a Bash Command

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

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

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

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


最简单的方法是使用SED(或Perl):

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

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

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

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

这会在每一行上循环,进行替换,并写入临时文件(不想破坏输入)。最后的移动只是将临时名称移动到原始名称。


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

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

-i标志指示它进行就地替换。

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


我很惊讶,因为我被这个绊倒了…

有一个"replace"命令随包"mysql server"一起提供,因此如果安装了该命令,请尝试:

1
2
3
4
5
# 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"

更多关于这方面的信息,请参阅"替换人"…


这是一篇古老的文章,但是对于任何想使用变量的人来说,正如@centurian所说的,单引号意味着什么都不会被扩展。

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

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


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

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

如果要读取一个文件并将其写入另一个文件,请在执行搜索/替换时使用sed:

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

如果要就地编辑文件(就像在编辑器中打开文件一样,编辑文件,然后保存文件),请向行编辑器"ex"提供说明。

1
2
3
4
echo"%s/abc/XYZ/g
w
q
"
| ex file

Ex就像没有全屏模式的vi。您可以在vi的":"提示下使用相同的命令。


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

1)SED和ED非常有用……用手!!!!看看@johnny的代码:

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

2)当我的限制是通过shell脚本使用它时,不能在内部使用变量来代替abc或xyz!这似乎至少和我理解的一致。所以,我不能使用:

1
2
3
4
5
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…",但不幸的是,这并不是故事的结尾。以下内容对我很有用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#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"'
'

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,那么当有一个包含*的字符串时,它会表现得很奇怪,如中所示。

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

成为

1
<VirtualHost ServerName www.example.com

没有结束角支架,Apache2甚至不能加载!

4)这种解析应该比点击搜索和替换慢,但是,正如您已经看到的,在一个解析周期中,有4个变量用于4种不同的搜索模式!

根据对问题的假定,我能想到最合适的解决方案。


可以使用SED

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

如果不确定要查找的文本是abcabcabcabc等,则使用-i表示忽略案例。

如果现在没有您的文件名,您可以使用findsed

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

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

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

您也可以使用ed命令在文件搜索和替换中执行以下操作:

1
2
3
# delete all lines matching foobar
ed -s test.txt <<< $'g/foobar/d
w'

在bash hacker网站上查看更多信息


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

如何操作的示例:

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

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


如果您正在处理的文件不太大,临时将其存储在变量中没有问题,那么您可以立即对整个文件使用bash字符串替换-不需要一行一行地遍历它:

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

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

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


尝试以下shell命令:

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


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

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

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

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

这里有几个ex的实际例子。

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

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

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

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

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

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

参见:

  • 如何以非交互方式编辑文件(例如,在管道中)?在六月份


您也可以在bash脚本中使用python。我在这里的一些顶级答案上没有太大的成功,并且发现这可以在不需要循环的情况下工作:

1
2
3
4
5
6
7
8
9
10
11
12
#!/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()

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

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

这不是一个明确的原因,但它是一个非常迅速和有用的。:)