in reply to Re: Script won't run
in thread Script wont run

Thank you Athanasius for your input and sample script, it works, but I have an issue with the formatting. the line below
print "Enter the plain text password for $name, ", "password is currently '$password'\n";
print out the entire line but I want only the name and password to show up it shows
Enter the plain text password for "Q9" Type="Container" Expanded="True +" Descr="" Icon="mRemote" Panel="General" Username="" Domain="", pass +word is currently '""'
I tried the following to just get the name and password only but it only returns 0 and 1 lol
my @names = grep /\bName=(.*?)/ , @array; my @password = grep /\bPassword=(.*?)/ , @array; my $newline = $line; # Make a copy print "name is $names[0] and password is $password[0]\n";
Also can you please tell me what the following means from your code.
?:\s+|$) /mgx also Q$password\E}

Replies are listed 'Best First'.
Re^3: Script won't run
by AnomalousMonk (Archbishop) on Nov 22, 2015 at 23:25 UTC
    ... can you please tell me what the following means from your code.

    ?:\s+|$) /mgx
    also

    Q$password\E}

    This should be  (?:\s+|$) (a non-capturing grouping),
    and  s{Password=\Q$password\E}{...} (the match regex of a substitution).

    To explain these:

    c:\@Work\Perl\monks>perl -wMstrict -le "use YAPE::Regex::Explain; ;; print YAPE::Regex::Explain->new('(?:\s+|$)')->explain; ;; my $password = 'f+o*o?[ ]b{}r'; my $regex = qr/Password=\Q$password\E/; print YAPE::Regex::Explain->new($regex)->explain; " The regular expression: (?-imsx:(?:\s+|$)) matches as follows: NODE EXPLANATION ---------------------------------------------------------------------- (?-imsx: group, but do not capture (case-sensitive) (with ^ and $ matching normally) (with . not matching \n) (matching whitespace and # normally): ---------------------------------------------------------------------- (?: group, but do not capture: ---------------------------------------------------------------------- \s+ whitespace (\n, \r, \t, \f, and " ") (1 or more times (matching the most amount possible)) ---------------------------------------------------------------------- | OR ---------------------------------------------------------------------- $ before an optional \n, and the end of the string ---------------------------------------------------------------------- ) end of grouping ---------------------------------------------------------------------- ) end of grouping ---------------------------------------------------------------------- The regular expression: (?-imsx:Password=f\+o\*o\?\[\ \]b\{\}r) matches as follows: NODE EXPLANATION ---------------------------------------------------------------------- (?-imsx: group, but do not capture (case-sensitive) (with ^ and $ matching normally) (with . not matching \n) (matching whitespace and # normally): ---------------------------------------------------------------------- Password=f 'Password=f' ---------------------------------------------------------------------- \+ '+' ---------------------------------------------------------------------- o 'o' ---------------------------------------------------------------------- \* '*' ---------------------------------------------------------------------- o 'o' ---------------------------------------------------------------------- \? '?' ---------------------------------------------------------------------- \[ '[' ---------------------------------------------------------------------- \ ' ' ---------------------------------------------------------------------- \] ']' ---------------------------------------------------------------------- b 'b' ---------------------------------------------------------------------- \{ '{' ---------------------------------------------------------------------- \} '}' ---------------------------------------------------------------------- r 'r' ---------------------------------------------------------------------- ) end of grouping ----------------------------------------------------------------------
    Remember that  \Q...\E metaquotes any non-\w character literal or interpolated character. See the  \Q \L \l \U \u \E interpolation control escape sequences in Quote and Quote-like Operators in perlop; see also quotemeta.


    Give a man a fish:  <%-{-{-{-<

      Thank you very much , this helped alot. I am still having a bit of trouble with the script,it will not update the file, unless $line = $newline; is inside the while loop

      while ($line =~ / Name=(.*?) \s+ Password=(.*?) (?:\s+|$) /mgx) { my $name = $1; my $password = $2; print "Enter the plain text password for $name, ", "password is currently '$password'\n"; chomp(my $newpass = <>); print "You will now swap $password for password='$newpass'\n"; print "Continue? (y/n)\n"; chomp(my $answer = <>); # Change the copy $newline =~ s{Password=\Q$password\E}{Password=$newpass} if $answer =~ /y/i; } $line = $newline; # Update this line in the file } untie @array or die "Cannot untie file '$filename': $!";

        As I explained above, you can’t put the assignment $line = $newline; inside the while loop, because updating $line resets the /g match position to the beginning of the string, creating an infinite loop. I tested my code before posting, and when the assignment $line = $newline; occurs after (i.e., outside) the inner loop, the tied file is updated correctly.

        I suspect your current problem is that the regex match is failing. In light of your subsequent post, it appears your data file never contains a string matching / Name=(.*?) \s+ Password=(.*?) (?:\s+|$) /mx. You will need to adapt the while loop regex to the actual format of the data in the input file. For example (untested):

        while ($line =~ / < Node \.*? Name="(.*?)" Type=".*?" Descr=".*?" Icon +=".*?" Panel=".*?" Username=".*?" Domain=".*?" Password="(.*?)" .*? > + /mgx)

        or just:

        while ($line =~ / < Node \.*? Name="(.*?)" .*? Password="(.*?)" .*? > +/mgx)

        Update (25th November, 2015): Changed / < \s* Name= to / < Node .*? Name= in each regex.

        Hope that helps,

        Athanasius <°(((><contra mundum Iustus alius egestas vitae, eros Piratica,

      I apologize,after analyzing I realized that the file would be updated after the while loop ends , I was pressing CTRL + C to see if the file is being written to correctly. I do have other questions if you can please bear with me:
      s{Password=\Q$password\E}{Password=$newpass} if $answer =~ /y/i;
      wrapping the old password with \Q\E means that it will remain unchanged evidenced by : The sentence remain unchanged, so why do \q\e instead of just doing s{$password}{$newpass}
      my $sentence = 'The quick brown fox jumped over the lazy dog'; my $substring = 'quick.*?fox'; $sentence =~ s{\Q$substring\E}{big bad wolf};
      The script I ended working with is , do you have any recommendations or critque, and also can you suggest how I could get it to update immediately to the file instead of waiting till the loop ends?
      #!/usr/bin/perl use strict; use warnings; use Tie::File; my $filename = '/home/stain/Downloads/REMOTEopen.xml'; tie my @array, 'Tie::File', $filename || die "Cannot tie file $filenam +e to array: $!"; foreach my $line (@array) { my $newline = $line; # Make a copy while ($line =~ / <Node \s+ Name="(.*?)" .*? Password="(.*?)" +.*? > /mgx) { my $name = $1; my $password = $2; print "Enter the plain text password for $name, ", "password is currently $password\n"; chomp(my $newpass = <>); print "You will now swap $password for $newpass\n"; print "Continue? (y/n)\n"; chomp(my $answer = <>); # Change the copy $newline =~ s{$password}{$newpass} if $answer =~ /y/i; } $line = $newline; # Update this line in the file } untie @array or die "Cannot untie file '$filename': $!";

        Hello again cbtshare,

        wrapping the old password with \Q\E means that it will remain unchanged ..., so why do \q\e instead of just doing s{$password}{$newpass}

        The purpose of adding \Q to a regex is to prevent any “special” regex characters from having their special meanings. For example, if the data contains:

        Password="abcde$"

        then without a \Q the $ will be interpreted to mean match the end of a line, which will cause the match to fail (unless abcde actually happens to come immediately before the end of a line, followed immediately by " at the beginning of the next line). But when preceded by a \Q, special characters like $, ., +, and * lose their special regex meanings and are treated as literal characters. That’s why, if there’s any possibility that a password may contain a character which the Perl regex engine considers to be “special,” it’s good practice to add \Q ... \E around the variable containing that password.

        can you suggest how I could get it to update immediately to the file instead of waiting till the loop ends?

        That depends on which loop you have in mind. As I explained above, the file can’t be updated within the inner (while) loop. However, the file is already being updated between each iteration of the outer (foreach) loop. You can see this by adding another <> as follows:

        use strict; use warnings; use Tie::File; $| = 1; my $filename = 'REMOTEopen.xml'; tie my @array, 'Tie::File', $filename or die "Cannot tie file $filename to array: $!"; for my $line (@array) { my $newline = $line; while ($line =~ / < Node \s* Name="(.*?)" .*? Password="(.*?)" .*? + > /mgx) { my $name = $1; my $password = $2; print "Enter the plain text password for $name, ", "password is currently $password\n"; chomp(my $newpass = <>); print "You will now swap $password for $newpass\n"; print "Continue? (y/n)\n"; chomp(my $answer = <>); $newline =~ s{\Q$password\E}{$newpass} if $answer =~ /y/i; } $line = $newline; print "Press <Enter> to continue..."; <>; } untie @array or die "Cannot untie file '$filename': $!";

        Assuming a file named “REMOTEopen.xml” is present in the current directory and contains data like this:

        <Node Name="MUN" Type="Connection" Descr="" Icon="mRemoteNG" Panel="Ge +neral" Username="root" Domain="" Password="a/5pqLSsMnvRfylxdN6coiFTdc +2+$"> </Node> <Node Name="ubunugit" Type="Connection" Descr="" Icon="mRemoteNG" Pane +l="General" Username="root" Domain="" Password="bBxKPKXIhw1wKEV9aEUN1 +yynoRGOWT$"> </Node>

        you can check the contents of the data file each time Press <Enter> to continue... is printed to the screen, and you should see that the file has indeed been updated as expected.

        Hope that helps,

        Athanasius <°(((><contra mundum Iustus alius egestas vitae, eros Piratica,

      Thank you for the help thus far, I managed to modify the script a bit an get it to grab the relevant info, but the issue now is that it is still not updating the file.Please have a look.
      #!/usr/bin/perl use strict; use warnings; use Tie::File; my $filename = '/home/stain/Downloads/REMOTEopen.xml'; tie my @array, 'Tie::File', $filename or die "Cannot tie file $filenam +e to array: $!"; foreach my $line (@array) { my $newline = $line; # Make a copy while ($line =~ / <Node \s+ Name="(.*?)" .*? Password="(.*?)" + .*? > /mgx) { my $name = $1; my $password = $2; print "Enter the plain text password for $name, ", "password is currently $password\n"; chomp(my $newpass = <>); print "You will now swap $password for $newpass\n"; print "Continue? (y/n)\n"; chomp(my $answer = <>); # Change the copy #$newline =~ s{Password=\Q$password\E}{Password=$newpass} if $ +answer =~ /y/i; $newline =~ s{\Q$password\E}{$newpass} if $answer =~ /y/i; } $line = $newline; # Update this line in the file } untie @array or die "Cannot untie file '$filename': $!";