xjlittle has asked for the wisdom of the Perl Monks concerning the following question:

Hello monks,

I have the following script where I want replace a line in my groups file with a new line containing new group members.
Here is my code:
#!/usr/bin/perl -w use strict; my $group = "sbo"; my $newstring = "sbo::18659:x0929,x1465,x1368,x5552; my $backup = "/etc/group"; my $new = "/etc/group.tmp"; #back up group `cp -f $backup $backup.4623`; #fix group open (GROUP, "<", $backup); open (NEW, ">", $new); while (<GROUP>){ s/^$group/$newstring/; print NEW $_; } close GROUP; close NEW; print "\n"; print "\n"; system("cat $backup |grep $group"); #system("ls -lad $dirs"); print "\n"; system("hostname"); print "\n";

Here is the original string and the output data. As you can see the output substitutes the string but then adds the :: and everything after it again. I haven't been able to figure out how to escape the :: to make it quit doing this. Please shed some light.
_DATA_ diff /etc/group /etc/group.tmp 104c104 < sbo::18659:x0929,x1465 --- > sbo::18659:x0929,x1465,x1368,x5552::18659:x0929,v1x1465,x1368,x5552 bash-2.05#
Thanks!

Replies are listed 'Best First'.
Re: How do I escape a :: in s///?
by ikegami (Patriarch) on Aug 14, 2009 at 16:10 UTC

    You only asked it to replace "sbo", not the whole line. I think you want

    s/^$group:.*/$newstring/;
    or the clearer
    $_ = "$newstring\n" if /^$group:/;

    I added ":" to the regex pattern so that you don't accidentally replace the group "sboadmin" (for example).

Re: How do I escape a :: in s///?
by JavaFan (Canon) on Aug 14, 2009 at 16:10 UTC
    As you can see the output substitutes the string but then adds the :: and everything after it again.
    No, it doesn't. You asked it to replace the sbo part, and only the sbo part, and that's what s/// did. If you want to replace the entire string, do something like:
    $_ = "$newstring\n" if /^$group/;
    instead.
Re: How do I escape a :: in s///?
by SuicideJunkie (Vicar) on Aug 14, 2009 at 16:11 UTC

    That is what you told it to do... replace only the $group (if it occurs at the start of a line) with $newstring. Everything other than the "sbo" is left untouched.

    Escaping :: doesn't make sense, and it doesn't need to be. Did you want to replace the whole line? If so you can do that with an if, or grab .* on the match side of your substitution.

Re: How do I escape a :: in s///?
by perlkiller (Acolyte) on Aug 14, 2009 at 18:13 UTC

    You are missing a double quote at the end of fifth line:

    my $newstring = "sbo::18659:x0929,x1465,x1368,x5552;

    should be

    my $newstring = "sbo::18659:x0929,x1465,x1368,x5552";
Re: How do I escape a :: in s///?
by perlkiller (Acolyte) on Aug 14, 2009 at 19:20 UTC

    Actually you should use File::Tie module. It is a better module to use. Try to avoid running shell commands if you can.

    #!/usr/bin/perl use warnings; use strict; use Tie::File; use File::Copy; use Sys::Hostname; my $host = hostname; my $group = 'sbo'; my $newstring = 'sbo::18659:x0929,x1465,x1368,x5552'; my $backup = "/tmp/groups"; copy ("$backup", "$backup.tmp") or die "can not copy $backup"; tie my @lines, 'Tie::File', "$backup" or die "can not open $backup $!" +; for (@lines) { if (/$group/) { $_ .= $newstring; last; } } untie @lines; print "\n $host \n";

      I disagree. It's a huge waste of resources and it doesn't simplify anything. Compare

      copy($groups_qfn, $backup_qfn) or die("Can't backup $groups_qfn\n"); tie(my @lines, 'Tie::File', $groups_qfn) or die("can not open $groups_qfn: $!\n"); for (@lines) { if (/^$group:/) { $_ = "$newstring\n"; last; } } untie @lines;
      with
      copy($groups_qfn, $backup_qfn) or die("Can't backup $groups_qfn\n"); open(my $in_fh, '<', $backup_qfn) or die("Can't open $backup_qfn: $!\n"); open(my $out_fh, '>', $groups_qfn) or die("Can't open $groups_qfn: $!\n"); while (<$in_fh>) { if (/^$group:/) { $_ = "$newstring\n"; } print $out_fh $_ }