使用 sed 搜索两行并在它们之前插入一行

使用 sed 搜索两行并在它们之前插入一行

源文件包含:

# 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 };

相关内容