sedを使用して2行を検索し、その前に1行を挿入します。

sedを使用して2行を検索し、その前に1行を挿入します。

ソースファイルには以下が含まれます。

# 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ファイルの正確な部分があることを確認するために、これらの2行を検索してから、これら2つの検索行の前に追加したい行を追加するにはどうすればよいですか?

ベストアンサー1

これはsedで実行するPITAですが、sedPerlではかなり簡単です(sedで可能であると100%確信していません...おそらくそうです。しかし、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正規表現マッチングに使用する場合、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...他の2つの改行文字を挿入します。

splice @lines, $found, 0, "\n", $insert, "\n\n";

@insertスカラー変数の代わりに配列を使用することもできます。ただし、1つだけを選択するのではなく、一意の要素の1つを確認するには$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 };

おすすめ記事