Regexp - find a value shown after string, (TCL) - regex

I want to return a value to $output, from out_buffer, so i did :
set output ""
set out_buffer {Unevictable: 0 kB}
#regexp -line {Unevictable:.* (.*\d).*KB} $out_buffer dummy output
if {!($output == "0")} {
return 0
} else {
puts "Unevictable is OK (equal 0)"
}
It works fine, but if out_buffer is like:
set out_buffer {cat /proc/meminfo | grep Unevictable
Unevictable: 0 kB
root#ltqcpe:/ramdisk/tmp# }
the return is null. What can I do ? that in any combination the value after Unevictable: will be put into $output.

You probably want to use the -line option to regexp so that ^ and $ are line-aware. (Maybe the -nocase option too.) Then you can do this (which I've tested with both your sample input strings):
regexp -line -nocase {^Unevictable:\s*(\d+)\s*kB$} $out_buffer -> size
Also remember to check the result of regexp; it's the number of times the RE matched, which is 0 or 1 (conveniently boolean!) unless you also pass in the -all option.

There can be many ways to write regular expressions to match your string. Try something like
if {regexp {Unevictable:\s+(\d+)\s+kB} $out_buffer ignore size } {
puts "size = $size"
}

Related

Searching for substring "52" in variable value "52/80" doesn't work in TCL

The code I'm using:
set channel 52/80
if {![ regexp { ([0-9]+)\/80 } $channel match cchannel ] } {
puts "Channel regex-ed in \[SLVR\]\[SetAffected_channels\] is: $cchannel\n\n"
}
returns error: can't read "cchannel": no such variable
Anything I'm missing here?
Your code is not matching because of the extra spaces.
% set channel 52/80
52/80
% regexp { ([0-9]+)\/80 } $channel match cchannel
0
% regexp {([0-9]+)\/80} $channel match cchannel
1
% set match
52/80
% set cchannel
52
You also shouldn't invert the result of regexp in this case; the if body script appears to be for when the pattern does match...

How to retrieve 1 (from M 1 COMPLD) this line using regexp in TCL?

set sample "act-user:IMLI:nmss:1::***;
imli 2013-10-21 15:13:54
M 1 COMPLD
;
IMLI 2013-10-21 15:13:54
;
>"
How to retrieve 1 (from M 1 COMPLD) this line using regexp in TCL ???
You need to use a non-default matching mode — line-aware — to make that RE simple:
regexp -line {^M\s+(\d+)\s+COMPLD$} $sample -> value
puts "value = $value"
Alternatively, you can put the option inside the RE itself:
regexp {(?n)^M\s+(\d+)\s+COMPLD$} $sample -> value
puts "value = $value"
The behaviour is exactly equivalent.

TCL Expect Using Regular Expressions to Extract String

I'm trying to extract strings from a file that match the following format:
AP[1st nibble].[2nd nibble].[3rd nibble]
For example: AP30f7.0df6.e51c
The code below captures all data sharing the same line as the above string. What can I do to stop capturing any undesired data found on the same line as the above string?
while { [gets $fchan inline] >= 0} {
switch -regexp -- $inline {
AP([a-f0-9]{4}\.[a-f0-9]{4}\.[a-f0-9]{4}) {
append default_name $inline\n
}
}
}
UPDATE:
Found a work around. Since each line matching the condition I've specified starts with the desired string, I'll use the string range command to extract only the first 16 characters.
while { [gets $fchan inline] >= 0} {
switch -regexp -- $inline {
AP([a-f0-9]{4}\.[a-f0-9]{4}\.[a-f0-9]{4}) {
set inline_mod [string range $inline 0 15]
append default_name $inline_mod\n
}
}
}
The switch command has some useful options when you want to do extraction at the same time as matching an RE. In particular, you should use the -matchvar option.
while { [gets $fchan inline] >= 0} {
switch -regexp -matchvar matched -- $inline {
AP([a-f0-9]{4}\.[a-f0-9]{4}\.[a-f0-9]{4}) {
# Extract the first and second elements
lassign $matched inline_mod triple
# With your sample of AP30f7.0df6.e51c
# $inline_mod is "AP30f7.0df6.e51c"
# $triple is "30f7.0df6.e51c"
append default_name $inline_mod\n
}
}
}
There are some further examples on that manual page.

In Perl, how many groups are in the matched regex?

I would like to tell the difference between a number 1 and string '1'.
The reason that I want to do this is because I want to determine the number of capturing parentheses in a regular expression after a successful match. According the perlop doc, a list (1) is returned when there are no capturing groups in the pattern. So if I get a successful match and a list (1) then I cannot tell if the pattern has no parens or it has one paren and it matched a '1'. I can resolve that ambiguity if there is a difference between number 1 and string '1'.
You can tell how many capturing groups are in the last successful match by using the special #+ array. $#+ is the number of capturing groups. If that's 0, then there were no capturing parentheses.
For example, bitwise operators behave differently for strings and integers:
~1 = 18446744073709551614
~'1' = Î ('1' = 0x31, ~'1' = ~0x31 = 0xce = 'Î')
#!/usr/bin/perl
($b) = ('1' =~ /(1)/);
print isstring($b) ? "string\n" : "int\n";
($b) = ('1' =~ /1/);
print isstring($b) ? "string\n" : "int\n";
sub isstring() {
return ($_[0] & ~$_[0]);
}
isstring returns either 0 (as a result of numeric bitwise op) which is false, or "\0" (as a result of bitwise string ops, set perldoc perlop) which is true as it is a non-empty string.
If you want to know the number of capture groups a regex matched, just count them. Don't look at the values they return, which appears to be your problem:
You can get the count by looking at the result of the list assignment, which returns the number of items on the right hand side of the list assignment:
my $count = my #array = $string =~ m/.../g;
If you don't need to keep the capture buffers, assign to an empty list:
my $count = () = $string =~ m/.../g;
Or do it in two steps:
my #array = $string =~ m/.../g;
my $count = #array;
You can also use the #+ or #- variables, using some of the tricks I show in the first pages of Mastering Perl. These arrays have the starting and ending positions of each of the capture buffers. The values in index 0 apply to the entire pattern, the values in index 1 are for $1, and so on. The last index, then, is the total number of capture buffers. See perlvar.
Perl converts between strings and numbers automatically as needed. Internally, it tracks the values separately. You can use Devel::Peek to see this in action:
use Devel::Peek;
$x = 1;
$y = '1';
Dump($x);
Dump($y);
The output is:
SV = IV(0x3073f40) at 0x3073f44
REFCNT = 1
FLAGS = (IOK,pIOK)
IV = 1
SV = PV(0x30698cc) at 0x3073484
REFCNT = 1
FLAGS = (POK,pPOK)
PV = 0x3079bb4 "1"\0
CUR = 1
LEN = 4
Note that the dump of $x has a value for the IV slot, while the dump of $y doesn't but does have a value in the PV slot. Also note that simply using the values in a different context can trigger stringification or nummification and populate the other slots. e.g. if you did $x . '' or $y + 0 before peeking at the value, you'd get this:
SV = PVIV(0x2b30b74) at 0x3073f44
REFCNT = 1
FLAGS = (IOK,POK,pIOK,pPOK)
IV = 1
PV = 0x3079c5c "1"\0
CUR = 1
LEN = 4
At which point 1 and '1' are no longer distinguishable at all.
Check for the definedness of $1 after a successful match. The logic goes like this:
If the list is empty then the pattern match failed
Else if $1 is defined then the list contains all the catpured substrings
Else the match was successful, but there were no captures
Your question doesn't make a lot of sense, but it appears you want to know the difference between:
$a = "foo";
#f = $a =~ /foo/;
and
$a = "foo1";
#f = $a =~ /foo(1)?/;
Since they both return the same thing regardless if a capture was made.
The answer is: Don't try and use the returned array. Check to see if $1 is not equal to ""

Powershell, how many replacements did you make?

I need to know how many replacements are made by Powershell when using either the -replace operator or Replace() method. Or, if that's not possible, if it made any replacements at all.
For example, in Perl, because the substitution operation returns the number of replacements made, and zero is false while non-zero is true in a boolean context, one can write:
$greeting = "Hello, Earthlings";
if ($greeting ~= s/Earthlings/Martians/) { print "Mars greeting ready." }
However with Powershell the operator and method return the new string. It appears that the operator provides some additional information, if one knows how to ask for it (e.g., captured groups are stored in a new variable it creates in the current scope), but I can't find out how to get a count or success value.
I could just compare the before and after values, but that seems entirely inefficient.
You're right, I don't think you can squeeze anything extra out of -replace. However, you can find the number of matches using Regex.Matches(). For example
> $greeting = "Hello, Earthlings"
> $needle = "l"
> $([regex]::matches($greeting, $needle)).Length # cast explicitly to an array
3
You can then use the -replace operator which uses the same matching engine.
After looking a little deeper, there's an overload of Replace which takes a MatchEvaluator delegate which is called each time a match is made. So, if we use that as an accumulator, it can count the number of replacements in one go.
> $count = 0
> $matchEvaluator = [System.Text.RegularExpressions.MatchEvaluator]{$count ++}
> [regex]::Replace("Hello, Earthlings","l",$matchEvaluator)
> $count
Heo, Earthings
3
Here a complete functional example which preserves the replacement behavior and count the number of matches
$Script:Count = 0
$Result = [regex]::Replace($InputText, $Regex, [System.Text.RegularExpressions.MatchEvaluator] {
param($Match)
$Script:Count++
return $Match.Result($Replacement)
})
None of the above answers are actually do replacement and working in recent PS versions:
James Kolpack - show how to count a removed regex (not replaced);
Kino101 - incomplete answer, variables not defined;
Annarfych - outdated answer, in recent PS version the evaluator count variable need to be global
Here is how you can do a replace and count it:
$String = "Hello World"
$Regex = "l|o" #search for 'l' or 'o'
$ReplaceWith = "?"
$Count = 0
$Result = [regex]::Replace($String, $Regex, { param($found); $Global:Count++; return $found.Result($ReplaceWith) })
$Result
$Count
Result in Powershell 5.1:
He??? W?r?d
5
Version of the script that actually does replace things and not null them:
$greeting = "Hello, earthlings. Mars greeting ready"
$counter = 0
$search = '\s'
$replace = ''
$evaluator = [System.Text.RegularExpressions.MatchEvaluator] {
param($found)
$counter++
Write-Output ([regex]::Replace($found, [regex] $search, $replace))
}
[regex]::Replace($greeting, [regex] $search, $evaluator);
$counter
->
> Hello,earthlings.Marsgreetingready
> 4