I have started exploring TextFSM by google, its great for screen scraping. But I'm stuck.
Here is the template for the command: "show ip community-list"
Value TYPE (Standard|Extended)
Value CLNAME (\D+)
Value SEQ (\d+)
Value ACTION (permit|deny)
Value ASNUM (\d+)
Value TAGNUM (\d+)
Start
^${TYPE}\s+Community\s+List\s+${CLNAME}\s+ -> Community
Community
^\s+${SEQ}\s+${ACTION}\s+${ASNUM}\s+:\s+${TAGNUM}\s+ -> Record Start
Raw output looks like:
Expanded Community List ROUTES_CL1
1 permit "11111:10000"
Standard Community List ROUTES_CL2
1 permit 11111:10000
2 permit 22222:10000
3 permit 33333:10000
somereason doesn't parse into key and values.
There are several things which need to be fixed:
Extended (in your template) vs Expanded (in your output)
CLNAME has digits in it which won't be matched by \D+
\s+:\s+ requires spaces around the colon which the output doesn't have
The Community List name needs to be remembered as there may be multiple action lines to which it applies. Use "Filldown".
Using this template:
Value Filldown TYPE (Standard|Extended|Expanded)
Value Filldown CLNAME (\w+)
Value SEQ (\d+)
Value ACTION (permit|deny)
Value ASNUM (\d+)
Value TAGNUM (\d+)
Start
^${TYPE}\s+Community\s+List\s+${CLNAME}
^\s*${SEQ}\s+${ACTION}\s+"?${ASNUM}:${TAGNUM}"? -> Next.Record
EOF
Will give the parsed output of:
[['Expanded', 'ROUTES_CL1', '1', 'permit', '11111', '10000'],
['Standard', 'ROUTES_CL2', '1', 'permit', '11111', '10000'],
['Standard', 'ROUTES_CL2', '2', 'permit', '22222', '10000'],
['Standard', 'ROUTES_CL2', '3', 'permit', '33333', '10000']]
A similar example with further explanation can be found on the textfsm wiki.
Related
The string is like this:
TEMPLATES="!$TEMPLATE templatename manufacturer model mode\n$TEMPLATE MacQuantum Wash Basic\n$$MANUFACTURER Martin\n$$MODELNAME Mac Quantum Wash\n$$MODENAME Basic\n"
My way to get strings without tags is:
local sentence=""
for word in string.gmatch(line,"%S+") do
if word ~= tag then
sentence=sentence .. word.." "
end
end
table.insert(tagValues, sentence)
E(tag .." --> "..sentence)
And I get output:
$$MANUFACTURER --> Martin
$$MODELNAME --> Mac Quantum Wash
...
...
But this is not the way I like.
I would like to find first the block starting with $TEMPLATE tag to check if this is the right block. There is many such blocks in a file I read line by line. Then I have to get all tags marked with double $: $$MODELNAME etc.
I have tried it on many ways, but none satisfied me. Perhaps someone has an idea how to solve it?
We are going to use Lua patterns (like regex, but different) inside a function string.gmatch, which creates a loop.
Explanation:
for match in string.gmatch(string, pattern) do print(match) end is an iterative function that will iterate over every instance of pattern in string. The pattern I will use is %$+%w+%s[^\n]+
%$+ - At least 1 literal $ ($ is a special character so it needs the % to escape), + means 1 or more. You could match for just one ("%$") if you only need the data of the tag but we want information on how many $ there are so we'll leave that in.
%w+ - match any alphanumeric character, as many as appear in a row.
%s - match a single space character
[^\n]+ - match anything that isn't '\n' (^ means invert), as many as appear in a row.
Once the function hits a \n, it executes the loop on the match and repeats the process.
That leaves us with strings like "$TEMPLATE templatename manufacturer"
We want to extract the $TEMPLATE to its own variable to verify it, so we use string.match(string, pattern) to just return the value found by the pattern in string.
OK: EDIT: Here's a comprehensive example that should provide everything you're looking for.
templates = "!$TEMPLATE templatename manufacturer model mode\n$TEMPLATE MacQuantum Wash Basic\n$$MANUFACTURER Martin\n$$MODELNAME Mac Quantum Wash\n$$MODENAME Basic\n"
local data = {}
for match in string.gmatch(templates, "%$+%w+%s[^\n]+") do --finds the pattern given in the variable 'templates'
--this function assigns certain data to tags inside table t, which goes inside data.
local t = {}
t.tag = string.match(match, '%w+') --the tag (stuff that comes between a $ and a space)
t.info = string.gsub(match, '%$+%w+%s', "") --value of the tag (stuff that comes after the `$TEMPLATE `. Explanation: %$+ one or more dollar signs $w+ one or more alphanumeric characters $s a space. Replace with "" (erase it)
_, t.ds = string.gsub(match, '%$', "") --This function emits two values, the first one is garbage and we don't need (hence a blank variable, _). The second is the number of $s in the string).
table.insert(data, t)
end
for _,tag in pairs(data) do --iterate over every table of data in data.
for key, value in pairs(tag) do
print("Key:", key, "Value:", value) --this will show you data examples (see output)
end
print("-------------")
end
print('--just print the stuff with two dollar signs')
for key, data in pairs(data) do
if data.ds == 2 then --'data' becomes a subtable in table 'data', we evaluate how many dollar signs it recognized.
print(data.tag)
end
end
print("--just print the MODELNAME tag's value")
for key, data in pairs(data) do
if data.tag == "MODELNAME" then --evaluate the tag name.
print(data.info)
end
end
Output:
Key: info Value: templatename manufacturer model mode
Key: ds Value: 1
Key: tag Value: TEMPLATE
-------------
Key: info Value: MacQuantum Wash Basic
Key: ds Value: 1
Key: tag Value: TEMPLATE
-------------
Key: info Value: Martin
Key: ds Value: 2
Key: tag Value: MANUFACTURER
-------------
Key: info Value: Mac Quantum Wash
Key: ds Value: 2
Key: tag Value: MODELNAME
-------------
Key: info Value: Basic
Key: ds Value: 2
Key: tag Value: MODENAME
-------------
--just print the stuff with two dollar signs
MANUFACTURER
MODELNAME
MODENAME
--just print the MODELNAME tag's value:
Mac Quantum Wash
This question already has answers here:
Zero-length string being returned from String#split
(2 answers)
Closed 6 years ago.
Why does .split create an empty character when its argument is the first letter of the string, and it doesn't do the same when the argument is the last letter of the string? In the second example, doesn't it "say", since nothing is on my right I'll output "" ? (Is there a 'nil' at the end of the string?)
I know this is not a very relevant question, however, I'd like to understand why the method behaves this way. Thank you!
string = "aware"
string.split("a") --> # outputs: ["", "w", "re"]
string.split("e") --> # outputs: ["awar"]
Below is a simple example of behavioral oddity that String#split may seem to have:
"1234".split(/1/) # => ["", "234"]
It seems like the expected result of the above example would be [“234”] since it is splitting on the 1, but instead we’re getting an unexpected empty string.
**
How String#split works
**
Internally String#split only uses regular expression delimiters. If you pass in a string delimiter it will be escaped for a regular expression and then turned into a regular expression:
1 2 3 4
"1234".split("1") # is really the same as "1234".split( Regexp.new( Regexp.escape("1") ) )
For the remainder of this article when I refer to delimiter I am referring to a regular expression delimiter since internally that is what String#split uses.
String#split keeps track the track of five important pieces of information:
the string itself
a results array which is returned
the position marking where to start matching the string against the
delimiter. This is the start position and is initialized to 0.
the position marking where the string matched the delimiter. This is
the matched position and is initialized to 0.
the position marking the offset immediately following where the
string matched the delimiter
String#split operates in a loop. It continues to match the string against the delimiter until there are no more matches that can be found. It performs the following steps on each iteration:
from the start position match the delimiter against the string
set the matchedposition to where the delimiter matched the string
if the delimiter didn’t match the string then break the loop
create a substring using the start and matched positions of the
string being matched. Push this substring onto the results array
set the start position for the next iteration
With this knowledge let’s discuss how String#split handles the previous example of:
"1234".split(/1/) # => ["", "234"]
the first loop
the start position is initialized to 0
the delimiter is matched against the string “1234”
the first match occurs with the first character, “1” which is at
position 0. This sets the matched position to 0.
a substring is created using the start and matched positions and
pushed onto our result array. This gives us string[start,end] which
translates to “1234”[0,0] which returns an empty string.
the start position is reset to position 1
The second loop
start is now 1
The delimiter is matched against the remainder of our string, “234”
No match is found so the loop is finished.
A substring is created using the start position and remainder of the
string and pushed onto the results array
the results array is returned
Given how String#split works it is easy to see why we have that unexpected empty string in our results array. You should note that this only occurred because the regular expression matched our string at the first character. Below is an example where the delimiter doesn’t match the first character and there is no empty string:
"1234".split(/2/) # => ["1", "34"]
The pickaxe book says of string#split
If the limit parameter is omitted, trailing empty fields are suppressed. ... If negative, there is no limit to the number of fields returned and trailing null [empty] fields are not suppressed. So:
irb(main):001:0> "aware".split('e')
=> ["awar"]
irb(main):002:0> "aware".split('e',-1)
=> ["awar", ""]
Suppose I have a url
xyz.com/param1=abc¶m2=123¶m3=!##
Each parameter can have many values like:
param1 = abc, xyz, qwe
param2 = 123, 456, 789
param3 = !##, $%^, &*(
All the parameters and values will be read from an excel file. There can be n number of parameters and each may have any number of values.
I want to generate all the combinations which can be formed using all values of each parameters.
Output will be like:
xyz.com/param1=abc¶m2=123¶m3=!##
xyz.com/param1=xyz¶m2=456¶m3=!##
xyz.com/param1=qwe¶m2=123¶m3=!##
xyz.com/param1=qwe¶m2=456¶m3=!##
xyz.com/param1=xyz¶m2=789¶m3=$%^
...
..
and so on
besides the previous comment in your post, you need construct some nested loops.
I will assume you have a bash shell available (since you hadn't specified your wanted language).
for I in 'abc' 'xyz' 'qwe'
do
for J in '123' '456' '789'
do
for K in '!##' '$%^' '&*('
do
echo "xyz.com/param1=${I}¶m2=${J}¶m3=${K}"
done
done
done
Note that:
that '&*(' will bring you problems, since & is the character that you use to delimit each parameter.
double quotes " around echo, will make a double quote character to make this program to fail miserably
the same apply to ', \ and some others
I am doing some client side validation in ASP.NET MVC and I found myself trying to do conditional validation on a set of items (ie, if the checkbox is checked then validate and visa versa). This was problematic, to say the least.
To get around this, I figured that I could "cheat" by having a hidden element that would contain all of the information for each set, thus the idea of a CSV string containing this information.
I already use a custom [HiddenRequired] attribute to validate if the hidden input contains a value, with success, but I thought as I will need to validate each piece of data in the csv, that a regular expression would solve this.
My regular expression work is extremely weak and after a good 2 hours I've almost given up.
This is an example of the csv string:
true,3,24,over,0.5
to explain:
true denotes if I should validate the rest. I need to conditionally switch in the regex using this
3 and 24 are integers and will only ever fall in the range 0-24.
over is a string and will either be over or under
0.5 is a decimal value, of unknown precision.
In the validation, all values should be present and at least of the correct type
Is there someone who can either provide such a regex or at least provide some hints, i'm really stuck!
Try this regex:
#"^(true,([01]?\d|2[0-4]),([01]?\d|2[0-4]),(over|under),\d+\.?\d+|false.*)$"
I'll try to explain it using comments. Feel free to ask if anything is unclear. =)
#"
^ # start of line
(
true, # literal true
([01]?\d # Either 0, 1, or nothing followed by a digit
| # or
2[0-4]), # 20 - 24
([01]?\d|2[0-4]), # again
(over|under), # over or under
\d+\.?\d+ # any number of digits, optional dot, any number of digits
| #... OR ...
false.* # false followed by anything
)
$ # end of line
");
I would probably use a Split(',') and validate elements of the resulting array instead of using a regex. Also you should watch out for the \, case (the comma is part of the value).
I've got a question on failure function description from "Compilers: Principles, Techniques, and Tools" aka DragonBook
Firstly, the quote:
In order to process text strings rapidly and search those strings for a keyword,
it is useful to define, for keyword b1b2...bn, and position s in that keyword , a failure function, f (s) ...
The objective is that b1b2.. - bf(s) is the longest proper prefix of
b1...bs, that is also a suffix of b1...bs. The reason f (s) is important is that
if we are trying to match a text string for blb2..bn, and we have matched the
first s positions, but we then fail (i.e., the next position of the text string does
not hold bs+l), then f (s) is the longest prefix of b1..bn that could possibly
match the text string up to the point we are at. Of course, the next character of
the text string must be bf(s)+1 or else we still have problems and must consider
a yet shorter prefix, which will be bf(f(s)).
So, the questions:
1. If we've matched s positions with the text, why f (s) is the longest prefix of b1..bn that matches the string? I think s - is the longest prefix.
2. Next character of the text string must be bf(s)+1, why? We have a mismatch at this position, does it matter at all what the char is?
f(s) is the longest prefix at that position that might match the entire keyword. The idea is not to try to match the keyword with the text from the start, but to find a position where the keyword appears.
Consider a search for the word 'aaaba' in the text 'aaaabaa'. The match fails after the three first a's, but it's not necessary to retry from the second 'a', since we know that if the next letter is a 'b' (which it is), we may have a match there.