Shell,查找包含的行,并仅替换它之后的行

Shell, find line containing, and replace on only line after it

我有一个文件位于:/Applications/Firefox.app/Contents/info.plist。我试图找到包含 LSUIElement 的行,然后用 <string>firefox</string>.

替换整行

这是我得到的,到目前为止,它什么也没做:

line=“LSUIElement”
rep="<string>firefox</string>"
sed -e "s/${line}/${rep}/g" /Applications/Firefox.app/Contents/info.plist > /Applications/Firefox.app/Contents/info.plist.profilist
mv /Applications/Firefox.app/Contents/info.plist.profilist /Applications/Firefox.app/Contents/info.plist 

使用 awk,你可以这样做:

awk '/LSUIElement/{i=NR+1}{if(NR==i){gsub(/1/,"0",[=10=])}}1' File > tmp && mv tmp File

逻辑: 如果找到 LSUIElement,将变量 i 设置为 NR+1(即下一行/记录编号)。作为第二部分,如果 NR(当前 record/line)是 i(之前保存的),则将 1 替换为 0。因此,替换只会发生在带有模式 LSUIElement.

的行之后的行中

让我们为那些手边没有这样的 plist 文件的人简化问题。

echo -e "foo bar baz\nthe suspicious LSUIElement in line 2\nand some more garbage" > luis.txt 
cat luis.txt
foo bar baz
the suspicious LSUIElement in line 2
and some more garbage

line=“LSUIElement”
rep="<string>firefox</string>"

所以这里我们有问题 1:sed 表达式中的斜杠通常像 's/a/b/g' 中那样分隔各部分。

sed -e "s/${line}/${rep}/g" luis.txt > luis2.txt 
mv luis2.txt luis.txt 

我们首先去寻找低垂的果实。 Sed,至少是 Gnu-sed,有一个选项 -i 来改变文件。在后台,可能有一个 正在制作副本,因为文件经常会缩小或增大,所以很难避免,但我们不需要重定向和 mv:

sed -i "s/${line}/${rep}/g" luis.txt

您甚至可以让 sed 生成备份文件。 AFAIK sed 在正常情况下不需要 -e ,但在这种情况下 gnu-sed 可能是特殊的。测试或阅读手册页。

现在是困难的部分。我们在没有 -i 的情况下进行测试以避免破坏。第一次测试,第一次惊喜:

line=“LSUIElement”
rep="<string>firefox</string>"
echo $line
# “LSUIElement”
echo $rep
# <string>firefox</string>

为什么 line 显示引号,而 rep 不显示?啊,这些是印刷引语?这有什么重要的吗?那你应该提一下,因为它很容易监督。我决定在没有他们的情况下离开,直到需要为止。

如果我们将 sed 表达式中的 / 替换为 |,我们可以避免与 $rep 变量中的 sed 语法冲突:

sed "s|${line}|${rep}|g" luis.txt
foo bar baz
the suspicious <string>firefox</string> in line 2
and some more garbage

这里我们完全替换了图案,而不是线条。

sed "s|.*${line}.*|${rep}|g" luis.txt
foo bar baz
<string>firefox</string>
and some more garbage

这是在替换整行。

sed "s|${line}.*|${rep}|g" luis.txt
foo bar baz
the suspicious <string>firefox</string>
and some more garbage

这是从包含模式到 EOL 的替换。

如果我们想在下一行中替换一些东西 - 那么替换什么?例如,垃圾?

我们可以在不替换 $line 的情况下匹配它,而是调用一系列简短的 2 命令,'n' for 'read next line' 并在那里执行我们的 s-substitution。这些命令必须包装到 {} 中。因为我们不需要将 / 更改为 |在前一部分,我们切换回那里:

sed "/${line}.*/{n;s|garbage|$rep|}" luis.txt
foo bar baz
the suspicious LSUIElement in line 2
and some more <string>firefox</string>

命令:

sed -i.bak "/${line}.*/{n;s|garbage|$rep|}" luis.txt

会创建这样的备份文件luis.txt.bak

有了一个模式,正如现在在评论中注意到的那样,它看起来像:

sed -i.bak "/${line}.*/{n;s|<string>.*</string>|$rep|}" luis.txt

确定它必须有多具体(1,数字,无关紧要)。