1

Source file consists of:

# 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

Search strings are:

if [ "`tty`" = "/dev/tty1" ] && [ -z "$DISPLAY" ] && [ "$USER" = "pi" ]; then
    bash "/opt/retropie/configs/all/autostart.sh"

I want to add rebootWithoutWiimotes=0 /home/pi/attachwii.sh before these lines so it looks like:

# 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

I have tried a lot of different sed commands and can get as far as sudo sed -i '\% bash "/opt/retropie/configs/all/autostart.sh"%i rebootWithoutWiimotes=0 /home/pi/attachwii.sh' 10-retropie.sh

This results in a incorrect file:

# 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

and if I try:

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

I get no change from the original file. It also doesn't work if I just do:

sudo sed -i '\% if [ "`tty`" = "/dev/tty1" ] && [ -z "$DISPLAY" ] && [ "$USER" = "pi" ]; then%i rebootWithoutWiimotes=0 /home/pi/attachwii.sh' 10-retropie.sh

I have suffered through a couple days of testing all sorts of different variations and learned quite a bit about sed and regular expressions but I can not figure this one out.

How can I make sed search for both of these lines so I know I have the exact correct section of the file and then add the line I wish to add before both of these search lines?

1
  • 1
    Escape the [ and ], or consider searching for the comment instead.
    – Kusalananda
    Jul 12, 2021 at 5:59

1 Answer 1

1

This is the sort of thing that's a PITA to do in sed, but is fairly easy in perl (I'm not even 100% sure it's possible in sed....it probably is, but I am sure that I wouldn't bother trying to do it with 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 };

The \Q and \E tell perl to escape any regex meta-characters when we use $insert in the regex match, so it is treated as a string literal.

Save it as, e.g., insert-attach.pl and make it executable with chmod +x insert-attach.pl and run it like so:

$ ./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

OK, that prints the modified file to stdout, but you want to replace the file. So, run it like this:

$ ./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

This will only replace the original file if it was successfully modified.

There are other ways of doing this in perl (some of which would be more appropriate if the input file was enormous, like gigabytes of text) but this is a good tutorial example of the kinds of things you can do with perl.


BTW, you can insert multiple lines if you want. The final arg to the splice function is a list.

e.g. this would insert a newline, the rebootWithout... line, and two more newlines.

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

You could even use an @insert array instead of a $insert scalar variable (but you'd need to modify the exit 1 if $lines[$i] =~ .... test to check for one of the more distinctive elements of @insert instead of just $insert - pick one that isn't going to be in the original file). For example:

#!/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 };
2
  • wow thank you so much. i was going crazy trying to figure this out with sed. i was thinking it may not even be possible. i remember things like this being so much easier when i was programming in c++ and basic back in the day. I dont know perl or much about what can be run in linux terminal for that matter. I was a dos guy since early 90s.
    – polygaryd
    Jul 12, 2021 at 7:01
  • if you're familiar with C, you shouldn't have too much difficulty picking up perl. awk is also worth learning (and it also has a C-like syntax).
    – cas
    Jul 12, 2021 at 7:08

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .