![使用 sed 搜索两行并在它们之前插入一行](https://linux22.com/image/192911/%E4%BD%BF%E7%94%A8%20sed%20%E6%90%9C%E7%B4%A2%E4%B8%A4%E8%A1%8C%E5%B9%B6%E5%9C%A8%E5%AE%83%E4%BB%AC%E4%B9%8B%E5%89%8D%E6%8F%92%E5%85%A5%E4%B8%80%E8%A1%8C.png)
源文件包含:
# launch our autostart apps (if we are on the correct tty and not in X)
if [ "`tty`" = "/dev/tty1" ] && [ -z "$DISPLAY" ] && [ "$USER" = "pi" ]; then
bash "/opt/retropie/configs/all/autostart.sh"
fi
搜索字符串是:
if [ "`tty`" = "/dev/tty1" ] && [ -z "$DISPLAY" ] && [ "$USER" = "pi" ]; then
bash "/opt/retropie/configs/all/autostart.sh"
我想rebootWithoutWiimotes=0 /home/pi/attachwii.sh
在这些行之前添加,使其看起来像:
# launch our autostart apps (if we are on the correct tty and not in X)
rebootWithoutWiimotes=0 /home/pi/attachwii.sh
if [ "`tty`" = "/dev/tty1" ] && [ -z "$DISPLAY" ] && [ "$USER" = "pi" ]; then
bash "/opt/retropie/configs/all/autostart.sh"
fi
我尝试了很多不同的sed
命令,并且可以达到sudo sed -i '\% bash "/opt/retropie/configs/all/autostart.sh"%i rebootWithoutWiimotes=0 /home/pi/attachwii.sh' 10-retropie.sh
这会导致文件不正确:
# launch our autostart apps (if we are on the correct tty and not in X)
if [ "`tty`" = "/dev/tty1" ] && [ -z "$DISPLAY" ] && [ "$USER" = "pi" ]; then
rebootWithoutWiimotes=0 /home/pi/attachwii.sh
bash "/opt/retropie/configs/all/autostart.sh"
fi
如果我尝试:
sudo sed -i '\% if [ "`tty`" = "/dev/tty1" ] && [ -z "$DISPLAY" ] && [ "$USER" = "pi" ]; then bash "/opt/retropie/configs/all/autostart.sh"%i rebootWithoutWiimotes=0 /home/pi/attachwii.sh%' 10-retropie.sh
我没有从原始文件中得到任何改变。如果我这样做,它也不起作用:
sudo sed -i '\% if [ "`tty`" = "/dev/tty1" ] && [ -z "$DISPLAY" ] && [ "$USER" = "pi" ]; then%i rebootWithoutWiimotes=0 /home/pi/attachwii.sh' 10-retropie.sh
我已经经历了几天的测试各种不同的变化,并了解了很多关于sed
正则表达式的知识,但我无法弄清楚这个。
如何sed
搜索这两行,以便我知道我拥有文件的完全正确部分,然后在这两个搜索行之前添加我希望添加的行?
答案1
这是 PITA 在 中要做的事情sed
,但在 perl 中相当容易(我什至不能 100% 确定它在 sed 中是可能的......它可能是,但我确信我不会打扰尝试用 sed 来做到这一点)。
#!/usr/bin/perl
use strict;
my $found = undef;
my $insert = "rebootWithoutWiimotes=0 /home/pi/attachwii.sh";
my @lines = (<>); # slurp entire file in @lines array
# iterate over @lines, looking for our two consecutive lines
foreach my $i (0 .. $#lines) {
# abort if we've already inserted the line
exit 1 if $lines[$i] =~ m:\Q$insert\E:;
if ( $lines[$i ] =~ m:if.*tty.*/dev/tty1.*DISPLAY.*USER.*pi:
&& $lines[$i+1] =~ m:bash "/opt/retropie/configs/all/autostart.sh":
) {
$found = $i;
last;
};
};
# if we found them, insert our new line immediately before them.
if (defined($found)) {
splice @lines, $found, 0, "$insert\n";
print @lines;
} else { exit 1 };
\Q
当我们在正则表达式匹配中使用时, and告诉perl\E
转义任何正则表达式元字符$insert
,因此它被视为字符串文字。
将其另存为,例如,insert-attach.pl
使其可执行并chmod +x insert-attach.pl
像这样运行:
$ ./insert-attach.pl 10-retropie.sh
# launch our autostart apps (if we are on the correct tty and not in X)
rebootWithoutWiimotes=0 /home/pi/attachwii.sh
if [ "`tty`" = "/dev/tty1" ] && [ -z "$DISPLAY" ] && [ "$USER" = "pi" ]; then
bash "/opt/retropie/configs/all/autostart.sh"
fi
好的,这会将修改后的文件打印到标准输出,但您想要替换该文件。所以,像这样运行它:
$ ./insert-attach.pl 10-retropie.sh > 10-retropie.sh.new \
&& mv -fv 10-retropie.sh.new 10-retropie.sh
renamed '10-retropie.sh.new' -> '10-retropie.sh'
$ cat 10-retropie.sh
# launch our autostart apps (if we are on the correct tty and not in X)
rebootWithoutWiimotes=0 /home/pi/attachwii.sh
if [ "`tty`" = "/dev/tty1" ] && [ -z "$DISPLAY" ] && [ "$USER" = "pi" ]; then
bash "/opt/retropie/configs/all/autostart.sh"
fi
这只会替换原始文件如果就修改成功了。
在 perl 中还有其他方法可以做到这一点(如果输入文件很大,比如千兆字节的文本,其中一些方法会更合适),但这是一个很好的教程示例,说明了可以使用 perl 执行的操作。
顺便说一句,如果需要,您可以插入多行。该函数的最后一个参数splice
是一个列表。
例如,这将插入一个换行符、该rebootWithout...
行和另外两个换行符。
splice @lines, $found, 0, "\n", $insert, "\n\n";
您甚至可以使用@insert
数组而不是$insert
标量变量(但是您需要修改测试exit 1 if $lines[$i] =~ ....
以检查更独特的元素之一,@insert
而不是仅仅$insert
选择一个不是将在原始文件中)。例如:
#!/usr/bin/perl
use strict;
my $found = undef;
my @insert = (
"\n",
"# run the attachwii.sh script",
"rebootWithoutWiimotes=0 /home/pi/attachwii.sh",
"\n",
"\n",
);
my @lines = (<>); # slurp entire file in @lines array
# iterate over @lines, looking for our two consecutive lines
foreach my $i (0 .. $#lines) {
# abort if we've already inserted the line
# (note: perl arrays start from 0, not 1, so $insert[2] is the third element)
exit 1 if $lines[$i] =~ m:\Q$insert[2]\E:;
if ( $lines[$i ] =~ m:tty.*/dev/tty1.*DISPLAY.*USER.*pi:
&& $lines[$i+1] =~ m:bash "/opt/retropie/configs/all/autostart.sh":
) {
$found = $i;
last;
};
};
# if we found them, insert our new lines immediately before them.
if (defined($found)) {
splice @lines, $found, 0, @insert;
print @lines;
} else { exit 1 };