Grep找到正确的行,用sed更改内容,然后将其放回原始文件中?


9

我正在尝试更改文件中特定行上的单个单词,但是将所有单词连接在一起时遇到了一些麻烦。

基本上,在文件的一行上有一个关键字“ firmware_revision”,并且在这一行(并且只有这一行)上,我想用“生产”一词代替“测试”一词。

所以我可以这样做:

grep 'firmware_revision' myfile.py | sed 's/test/production'

这将挑选出我想要的行并执行替换,但是我不知道如何将新行插入原始文件以替换旧行。我显然不能仅仅将其重定向回文件,那么该怎么办?

即使我使用临时文件,通过使用临时grep文件,我也需要丢失文件中的所有其他数据,因此,我不再只能将所有文件重定向到临时文件,而是用临时文件替换原始文件。

编辑-有人要求更多信息

可以说我有一个像这样的文件

[
  ('key_name1', str, 'value1', 'Description'),
  ('key_name2', str, 'value2', 'Description'),
  ('key_name3', str, 'value3', 'Description'),
  ('firmware_revision', str, 'my-firmware-name-test', 'Firmware revision name')
]

现在,我想编写一个脚本(最好是单行代码),该脚本将找到包含“ firmware_revision”的行,并将该行中“ test”一词的所有实例更改为“ production”。“测试”一词可能在该文件的其他位置,我不希望更改这些内容。为了清楚起见,我想将上面的行更改为

('firmware_revision', str, 'my-firmware-name-production', 'Firmware revision name')

我该怎么做呢?


5
sed是非常强大的功能,它既可以执行功能(也可以执行grep替代功能),但是我们需要有关该行外观的更多信息以帮助您。
grochmal

我会在原始帖子中添加更多信息
John Allard

7
试试:sed -i.bak '/firmware_revision/ s/test/production/' myfile.py
John1024 '16

1
啊,效果很好!你能解释一下语法吗?
John Allard

@JohnAllard很好。我添加了一个带有解释的答案。
John1024 '16

Answers:


22

尝试:

sed -i.bak '/firmware_revision/ s/test/production/' myfile.py

在此,/firmware_revision/作为条件。对于与正则表达式匹配的行为firmware_revisiontrue,对于其他行为false。如果条件为真,则执行以下命令。在这种情况下,该命令是一个替换命令,取代的第一次出现testproduction

换句话说,该命令s/test/production/仅在与regex匹配的行上执行firmware_revision。所有其他行均不变。

默认情况下,sed将其输出发送到标准输出。但是,您想更改该文件。因此,我们添加了该-i选项。特别是,-i.bak使文件更改为带有.bak扩展名的备份副本。

如果您确定该命令对您有用,并且您想过着危险的生活并且不想创建备份,那么使用GNU sed(Linux),请使用:

sed -i '/firmware_revision/ s/test/production/' myfile.py

相反,在BSD(OSX)上,该-i选项必须具有参数。如果您不想保留备份,请为其提供一个空参数。因此,使用:

sed -i '' '/firmware_revision/ s/test/production/' myfile.py

编辑

在对问题的编辑中,OP要求用替换每次出现的。在这种情况下,我们将选项添加到替代命令中以进行全局(针对该行)替换:testproductiong

sed -i.bak '/firmware_revision/ s/test/production/g' myfile.py

5
为了完整起见,您可能还希望解释一下该-i.bak部分。
史蒂芬·哈里斯

5
@StephenHarris好点。-i添加的说明。
John1024 '16

我觉得您可能可以站到星星上,只是站在书上解释所有sed可以做的事...
KlaymenDK '16

@ John1024对于不是BSD的人,您能补充一下''之后的第一个原因 -i吗?
哈斯图

3
OP希望将该行上所有“测试”一词的实例更改为“生产”。在这种情况下sed -i '/firmware_revision/ s/test/production/g' myfile.py,将/ g附加到sed命令很重要:,否则仅更改第一个实例。
knub

4

在较旧的老式计算机上,sed该计算机不支持option -i

TF=$( mktemp -t "${0##*/}"_$$_XXXXXXXX ) && \
trap 'rm -f "$TF"' EXIT HUP INT QUIT TERM && \
sed '/firmware_revision/ s/test/production/' myfile.py >"$TF" && \
mv -f "$TF" myfile.py

1
在这些较旧的计算机上,您可以简单地使用ed并在一行中完成操作:printf %s\\n g/firmware_revision/s/test/production/g w q | ed -s myfile.py
don_crissti

1
@don_crissti确实如此,但ed(1)没有提供任何借口来显示的使用mktemp(1)
佐藤桂

如果您使用的是临时文件,则最好与原始文件一样保留原始文件的inode和perms ed。代替mv -f "$TF" myfile.pl使用cat "$TF" > myfile.pl && rm -f "$TF"。顺便说一句,使用小写变量名($tf而不是$TF)是一个好习惯,可以保证它们不与任何bash内置变量(也可能是其他bourne shell)冲突。
cas

@cas Re::cat "$TF" > myfile.pl尝试一下:(touch a b; chmod 0444 a; cat b >a顺便说一句,sed -i在这种情况下也不会工作)。最好让用户处理。回复::rm -f "$TF"不需要,请参阅trap ... EXIT。回复:$tf代替$TF:也许;这是风格问题。
佐藤桂

这是unix文件权限的正常和预期行为。我的观点是,创建一个新文件并将其移动到原始文件上将1.为该文件名生成一个新的inode(断开所有硬链接)。2.如果电流umask 不同于原始电导,则可能会更改电导。至于大写var,这不仅仅是样式问题-许多错误地使用大写PATH或RANDOM或SHELL或自己的变量的人都发现了难题。(是的,我经常自己使用大写vars。我知道这是错误的,我正试图打破习惯)。
cas
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.