{"id":22513386,"url":"https://github.com/bitvantage/sharptextfsm","last_synced_at":"2026-03-01T04:03:18.415Z","repository":{"id":245108053,"uuid":"817491655","full_name":"Bitvantage/SharpTextFsm","owner":"Bitvantage","description":"SharpTextFSM is a .NET implementation of the Google TextFSM Python module.","archived":false,"fork":false,"pushed_at":"2024-11-19T02:39:25.000Z","size":1092,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2024-11-19T03:08:06.632Z","etag":null,"topics":["cli","parser","parsing","regexp","state-machine","textfsm"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"agpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Bitvantage.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-06-19T20:39:57.000Z","updated_at":"2024-11-19T02:39:29.000Z","dependencies_parsed_at":"2024-06-24T19:17:13.849Z","dependency_job_id":"8fccb4fb-0c61-478e-894f-785d8a3edeab","html_url":"https://github.com/Bitvantage/SharpTextFsm","commit_stats":null,"previous_names":["bitvantage/sharptextfsm"],"tags_count":6,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Bitvantage%2FSharpTextFsm","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Bitvantage%2FSharpTextFsm/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Bitvantage%2FSharpTextFsm/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Bitvantage%2FSharpTextFsm/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Bitvantage","download_url":"https://codeload.github.com/Bitvantage/SharpTextFsm/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":228552373,"owners_count":17935803,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cli","parser","parsing","regexp","state-machine","textfsm"],"created_at":"2024-12-07T03:12:08.334Z","updated_at":"2025-11-01T12:04:56.661Z","avatar_url":"https://github.com/Bitvantage.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Bitvantage.SharpTextFsm\nSharpTextFsm is a .NET implementation of the [Google TextFSM Python module](https://github.com/google/textfsm). TextFSM templates match semi-formated line delimited text using an articulated regular expression state machine.\nTextFSM templates are particularly well suited for parsing CLI output.\n\n## Installing via NuGet Package Manager\n```\nPM\u003e NuGet\\Install-Package Bitvantage.SharpTextFsm\n```\n\n## TextFSM Resources\nThis documentation is focused on details that are specific to SharpTextFSM, for general information on TextFSM templates or regular expressions the following resources may be helpful:\n* [Google TextFSM Wiki](https://github.com/google/textfsm/wiki/TextFSM)\n* [Getting Started with TextFSM for Python](https://pyneng.readthedocs.io/en/latest/book/21_textfsm/README.html)\n* [Awesome Regex Resources](https://github.com/Varunram/Awesome-Regex-Resources)\n* [Network to Code Text FSM Templates](https://github.com/networktocode/ntc-templates)\n  \\\n  An extensive library of templates for many vendors' equipment that can be used directly by SharpTextFSM.\n\n## Quick Start\nA simple example of how to parse the output from the 'show ip arp' command from a Cisco IOS switch into a C# record.\n\n### Combined C# Record Object and TextFSM Template \n```csharp\nrecord ShowIpArp : ITemplate\n{\n    public string Protocol { get; set; }\n    public IPAddress IpAddress { get; set; }\n    [Variable(ThrowOnConversionFailure = false)]\n    public long? Age { get; set; }\n    public string MacAddress { get; set; }\n    public string Type { get; set; }\n    public string Interface { get; set; }\n\n    string ITemplate.TextFsmTemplate =\u003e\n        \"\"\"\n        Value PROTOCOL (\\S+)\n        Value IP_ADDRESS (\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3})\n        Value AGE (-|\\d+)\n        Value MAC_ADDRESS ([a-f0-9]{4}\\.[a-f0-9]{4}\\.[a-f0-9]{4})\n        Value TYPE (\\S+)\n        Value INTERFACE (\\S+)\n            \n        Start\n         ^Protocol\\s+Address\\s+Age\\(min\\)\\s+Hardware Addr\\s+Type\\s+Interface -\u003e Entry\n         ^.* -\u003e Error\n            \n        Entry\n         ^${PROTOCOL}\\s+${IP_ADDRESS}\\s+${AGE}\\s+${MAC_ADDRESS}\\s+${TYPE}(\\s+${INTERFACE})?$$ -\u003e Record\n         ^.* -\u003e Error\n        \"\"\";\n}\n```\n### Example Usage\n```csharp\nclass Example\n{\n    public void Test()\n    {\n        var data = \"\"\"\n            Protocol  Address              Age(min)       Hardware Addr     Type      Interface\n            Internet  172.16.233.229       -              0000.0c59.f892    ARPA      Ethernet0/0\n            Internet  172.16.233.218       -              0000.0c07.ac00    ARPA      Ethernet0/0\n            Internet  172.16.233.19        -              0000.0c63.1300    ARPA      Ethernet0/0\n            Internet  172.16.233.209       -              0000.0c36.6965    ARPA      Ethernet0/0\n            Internet  172.16.168.11        -              0000.0c63.1300    ARPA      Ethernet0/0\n            Internet  172.16.168.254       9              0000.0c36.6965    ARPA      Ethernet0/0\n            Internet  10.0.0.0             -              aabb.cc03.8200    SRP-A\n            \"\"\";\n\n        var template = Template.FromType\u003cShowIpArp\u003e();\n        var results = template.Run\u003cShowIpArp\u003e(data).ToList();\n    }\n}\n```\n### Results\n```text\n{ShowIpArp { Protocol = Internet, IpAddress = 172.16.233.229,   Age = -, MacAddress = 0000.0c59.f892, Type = ARPA,  Interface = Ethernet0/0 }}\n{ShowIpArp { Protocol = Internet, IpAddress = 172.16.233.218,   Age = -, MacAddress = 0000.0c07.ac00, Type = ARPA,  Interface = Ethernet0/0 }}\n{ShowIpArp { Protocol = Internet, IpAddress = 172.16.233.19,    Age = -, MacAddress = 0000.0c63.1300, Type = ARPA,  Interface = Ethernet0/0 }}\n{ShowIpArp { Protocol = Internet, IpAddress = 172.16.233.209,   Age = -, MacAddress = 0000.0c36.6965, Type = ARPA,  Interface = Ethernet0/0 }}\n{ShowIpArp { Protocol = Internet, IpAddress = 172.16.168.11,    Age = -, MacAddress = 0000.0c63.1300, Type = ARPA,  Interface = Ethernet0/0 }}\n{ShowIpArp { Protocol = Internet, IpAddress = 172.16.168.254,   Age = 9, MacAddress = 0000.0c36.6965, Type = ARPA,  Interface = Ethernet0/0 }}\n{ShowIpArp { Protocol = Internet, IpAddress = 10.0.0.0,         Age = -, MacAddress = aabb.cc03.8200, Type = SRP-A, Interface =             }}\n```\n## 'Value' Bindings\nTemplate TextFSM Values are automatically bound to similarly named public fields and properties within the type.\n\nBy default the case and the '_' are ignored. This behavior can be changed by decorating the class with the TemplateAttribute.\n\n\n| Flag Name     | Description                                                    |\n| ----          | -----------                                                    |\n| Disabled      | Do not automatically map. Mapping must be done explicitly      |\n| Exact         | Exactly match the 'Value' name with the field or property name |\n| IgnoreCase    | Perform a case-insensitive match                               |\n| SnakeCase     | Ignore '_'                                                     |\n\nFor example to match using exact match logic and case-insensitive logic:\n```csharp\n[Template(MappingStrategy.Exact | IgnoreCase)]\nrecord Test\n{\n  ...\n}\n```\n\n### Explicit 'Value' Binding\nA field or property can be explicitly bound using the VariableAttribute. Explicitly bound fields and properties take precedence over automatically bound fields and properties.\n```csharp\n[Variable(Name = \"MY_VALUE_NAME\")]\npublic long MagicNumber { get; set; }\n```\n\n### Ignoring a Field or Property\nA field or property can be ignored by setting the Ignore flag in the VariableAttribute.\n```csharp\n[Variable(Ignore = true)]\npublic long MagicNumber { get; set; }\n```\n\n## Type Conversion\nIf a type converter is not specified and the underlying type has a TryParse() or Parse() method, the built-in GenericTryParseConverter or GenericParseConverter type converter will automatically be configured.\n\n### Setting an Explicit Type Converter\n```csharp\n[Variable(Converter = typeof(MyTypeConverter)]\npublic long ValueField { get; set; }\n```\n### Type Conversion Failure\n If the type conversion fails, a TemplateTypeConversionException exception will be thrown. This behavior can be changed by setting the ThrowOnConversionFailure property to false.\n\nWhen the ThrowOnConversionFailure property is set to false, values that fail to parse are set to the type's default value. This value can be changed by setting the DefaultValue property. The default value is specified as a string value and will be converted using the associated type converter.\n```csharp\n[Variable(ThrowOnConversionFailure=false)]\npublic long? Length { get; set; }\n\n[Variable(ThrowOnConversionFailure=false)]\npublic long MagicNumber { get; set; }\n\n[Variable(ThrowOnConversionFailure=false, DefaultValue=\"42\")]\npublic long Answer { get; set; }\n```\n### Built-In Type Converters\n| Name                      | Description                                                                                      |\n| ----                      | -----------                                                                                      |\n| AnyValueAsFalseConverter  | Converts non-empty values to false                                                               |\n| AnyValueAsTrueConverter   | Converts non-empty values to true                                                                |\n| EnumConverter             | Automatically used for enum types                                                                |\n| GenericParseConverter     | Automatically used for types with a Parse                                                        |\n| GenericTryParseConverter  | Automatically used for types with a TryParse method                                              |\n| StringConverter           | Automatically used for string types                                                              |\n| TerseTimeSpanConverter    | Converts timespans in the format of 1y2w3h4m5s or 1 year, 2 weeks, 3 hours, 4 minutes, 5 seconds | \n\n### Enum Conversion\nBy default, enums are automatically parsed using the built-in type converter EnumConverter. Member values are matched in a case-insensitive way, and aliases can be attached by using the EnumAliasAttribute.\n\n```csharp\nenum Animal\n{\n    None,\n    Armadillo,\n    Blobfish,\n    Capybara,\n    Fossa,\n    [EnumAlias(\"Ghost Shark\")]\n    GhostShark,\n    [EnumAlias(\"Goblin Shark\")]\n    GoblinShark,\n    Hagfish,\n    Hoatzin,\n    Pangolin,\n    Platypus,\n    [EnumAlias(\"Sea Hog\")]\n    [EnumAlias(\"Sea Pig\")]\n    [EnumAlias(\"Sea Piggy\")]\n    [EnumAlias(\"Sea Swine\")]\n    SeaPig,\n    Sloth,\n    Tarsier,\n    Uakari,\n}\n```\n### Custom Type Converters\nA custom type converter can be set in the Converter property.\n```csharp\n[Variable(Converter=typeof(MyConverter))]\npublic long MagicNumber { get; set; }\n```\nTo create a custom type converter extend ValueConverter\\\u003cT\\\u003e.\n```csharp\npublic class MyConverter : ValueConverter\u003clong\u003e\n{\n    public override bool TryConvert(string value, out long convertedValue)\n    {\n        if(!int.TryParse(value, out var parsedValue))\n        {\n            convertedValue = 0;\n            return false;\n        }\n\n        convertedValue = 42 + parsedValue;\n        return true;\n    }\n}\n```\n\n### List Conversion\nWhen the TextFSM value definition has the 'List' option set, the underlying collection is an array, generic List\\\u003cT\\\u003e, or a ReadOnlyCollection\\\u003cT\\\u003e, and the ListCreator property is not set a list creator is automatically assigned. The list creator is responsible for creating an object of the target type from an array of typed values.\n\n### Setting Explicit Type Converters\n```csharp\n[Variable(Converter = typeof(ListCreator)]\npublic string Value { get; set; }\n```\n### Custom List Creator\nTo create a custom type converter extend from ListCreator\\\u003cTList, TItem\\\u003e.\n```csharp\nclass CommaSeparatedList : ListCreator\u003cstring,string\u003e\n{\n    public override string Create(string[] values)\n    {\n        return string.Join(\",\", values);\n    }\n}\n```\n### Built-in List Creators\n| Name                      | Description                                       |\n| ----                      | -----------                                       |\n| ArrayConverter            | Converts TestFSM lists to an array                |\n| GenericListCreator        | Converts TestFSM lists to a generic list          |\n| ReadOnlyCollectionCreator | Converts TestFSM lists to a read-only collection  |\n\n### Transformers\nBefore a value is converted and assigned, it can be transformed from one value to another using the ValueTransformerAttribute.\n```csharp\n[ValueTransformer(\"old value\", \"new value\")] // \"old value\" -\u003e \"new value\"\n[ValueTransformer(\"-\", null)]                // Value is not set if the string value is null\n[ValueTransformer(\"*\", \"\")]                  // Value is not set if SkipEmpty is true (default) or empty if SkipEmpty is false\npublic string Value { get; set; }\n```\n\n## Raw Rows\nThe TextFSM row that is used to generate the record can be included in the record. This can be useful for reference purposes or used by the internal logic of the class.\n\n```csharp\n[RawRow]\npublic Row ExampleRawRow { get; set; }\n```\n\n## Validation and Post Processing\nWhen the ITemplateValidator interface is implemented, a custom method is called after the record is created. The record can be modified, and post-processing tasks can be performed. The raw row that was used to generate the instance of the record is provided for post-processing purposes.\n\nIf the function returns false, the record will not be added to the result set.\n\n```csharp\ninternal class Test : ITemplate, ITemplateValidator\n{\n    public long MyProperty { get; set; }\n\n    ...\n\n    bool ITemplateValidator.Validate(Row row)\n    {\n        if(MyProperty == 0)\n            return false;\n\n        if(MyProperty == 5)\n            MyProperty = int.Parse((string)row[\"MyUnboundValue\"]);\n\n        if(MyProperty \u003e 10)\n            MyProperty = 10;\n           \n        return true;\n    }\n}\n```\n\n## Regex Value Defenition\nRegular expression values function similarly to regular values but do not implicitly capture. They can be used in both rules and other values, enhancing readability and reducing the need for repeating the same expressions.\n\n```text\nValue Regex CUTE_ANIMALS (dog|cat|panda|rabit)\nValue Regex UGLY_ANIMALS (pug)\nValue Regex ALL_ANIMALS (${CUTE_ANIMALS}|${UGLY_ANIMALS})\nValue EVERYTHING (${ALL_ANIMALS})\n\nStart\n ^${CUTE_ANIMALS}\n ^${UGLY_ANIMALS}\n ^${ALL_ANIMALS}\n ^${EVERYTHING}\n```\n\\* This feature is not present in the reference implementation of TextFSM.\n\n### Library Patterns\nCommon patterns are available in the built-in Regex library and may be referenced just like they were explicitly defined. Many of the patterns were either borrowed from or inspired by [Grok](https://github.com/logstash-plugins/logstash-patterns-core/blob/main/patterns/ecs-v1/grok-patterns).\n```\nValue MAC_ADDRESS (${_MAC_ADDRESS})\n\nStart\n ^MAC_ADDRESS -\u003e Record\n```\n\n| Name | Description |\n| ---- | ----------- |\n|_BASE_10_NUMBER|Matches decimal numbers|\n|_BASE_16_FLOAT|Matches hexadecimal floating-point numbers|\n|_BASE_16_NUMBER|Matches hexadecimal numbers|\n|_DATA|Lazy match zero or more characters|\n|_DATE_EU|Matches dates in the day-month-year, day/month/year or day.month.year format|\n|_DATE_US|Matches dates in the month-day-year or month/day/year format|\n|_DATE|Matches dates that are in the US or EU format|\n|_DAY|Matches weekdays that are in the abbreviated or full-name format|\n|_EMAIL_ADDRESS|Matches email addresses|\n|_EMAIL_LOCAL_PART|Matches the characters before the at sign (@) in an email address. For example, in the email address 123456@alibaba.com, the matched content is 123456|\n|_GREEDY_DATA|Greedy match zero or more characters|\n|_HOST_AND_PORT|Matches IP addresses, hostnames, or positive integers|\n|_HOSTNAME|Matches hostnames\n|_HOUR|Matches hours|\n|_INTEGER|Matches integers|\n|_IP_OR_HOST|Matches IP addresses or hostnames|\n|_IP|Matches IPv6 or IPv4 addresses|\n|_IPV4|Matches IPv4 addresses|\n|_IPV6|Matches IPv6 addresses|\n|_MAC_ADDRESS|Matches any MAC addresses format |\n|_MAC_ADDRESS_QUAD_DOT|Matches a MAC address in the 0102.03ab.cdef format |\n|_MAC_ADDRESS_QUAD_COLON|Matches a MAC address in the 0102:03ab:cdef format |\n|_MAC_ADDRESS_DOUBLE_DOT|Matches a MAC address in the 01.02.03.ab.cd.ef format |\n|_MAC_ADDRESS_DOUBLE_DASH|Matches a MAC address in the 01-02-03-ab-cd-ef format |\n|_MAC_ADDRESS_DOUBLE_COLON|Matches a MAC address in the 01:02:03:ab:cd:ef format |\n|_MINUTE|Matches minutes|\n|_MONTH_DAY|Matches days in a month|\n|_MONTH_NUMBER|Matches months that are in the numeric format|\n|_MONTH|Matches months that are in the numeric, abbreviated, or full-name format|\n|_NON_NEGATIVE_INTEGER|Matches non-negative integers|\n|_NOT_SPACE|Matches characters that are not spaces|\n|_NUMBER|Matches numbers|\n|_POSITIVE_INTEGER|Matches positive integers|\n|_QUOTED_STRING|Matches quoted content. For example, in the I am \"Iron Man\" string, the matched content is Iron Man.\n|_SECOND|Matches seconds|\n|_SPACE|Matches spaces|\n|_TIME|Matches time|\n|_URN|Matches uniform resource names (URN)\n|_UUID|Matches universally unique identifiers (UUIDs)\n|_WORD|Matches letters, digits, and underscores (_)|\n|_YEAR|Matches years|\n\n## Loop Free Continue Line Action with State Change\nThe reference implementation of TextFSM does not allow combining the 'Continue' line action with a state transition to ensure a loop-free state machine. While this restriction is both reasonable and well intended, it complicates numerous common use cases and discourages using states to validate data.\n\nSharpTextFsm relaxes this restriction by allowing 'Continue' line actions with a state transition provided that doing so cannot produce a loop.\n\nA state machine has a loop if it CAN indefinitely process the same line without advancing to the next line. In practice, this means there cannot be a state with a rule that includes a continue line action and a state transition that jumps to another state that can return to the previous state, either directly or indirectly, through rules that specify the continue line action.\n\nThe following is an example of a valid SharpTextFsm template that uses both the Continue line action and a state change:\n```\nState1\n ^.* -\u003e Continue State2\nState2\n ^.* -\u003e Continue State3\n ^.* -\u003e State1\nState3\n ^X.* -\u003e Record\n ^Y.* -\u003e State1\n```\n\n\\* This feature is not present in the reference implementation of TextFsm. \n\n## ~Global State\nRules in the ~Global state are evaluated each time a new line is read and before the rules associated with the current state.\n\nPlacing rules in the ~Global state can be useful for handling content that appears in numerous states, such as comments, and for transitioning to states when the previous state ends without a clear trigger.\n\nSince rules cannot transition to the ~Global state, rules in the ~Global state can not create a loop, therefore, using the 'Continue' line action with a state transition is always allowed.\n\n```csharp\nValue Test (.*)\n\n~Global\n ^\\s*#\n ^X -\u003e Continue XState\n\nStart\n ^.*\n\nXState\n ^X${Test}\n```\n\n\\* This feature is not present in the reference implementation of TextFsm.\n### ~Global State Filters\nState filters can be attached to rules in the ~Global state to limit the states that the rule applies to. The list of states can be negated by prefixing the state list with a '^'.\n\n```csharp\n~Global\n [State1,State2,State3]\n ^X -\u003e Continue State4\n\n [^State1,State2]\n ^Y -\u003e State1\n```\n\n## Metadata Value Option\nUsing the 'Metadata' value option, state machine data can be included in the result set.\n\nNormally, the last value before a row is recorded will appear in the result set. However, if the List option is specified, an entry is added to the list each time a rule is matched.\n```csharp\nValue Metadata LAST_LINE_NUMBER (Line)\nValue Metadata,List INPUT_TEXT (Text)\n```\n\\*  This feature is not present in the reference implementation of TextFsm.\n### Metadata Patterns\nThe metadata pattern is a value from the below list and not a regular expression.\n\n| Name           | Description                        |\n| ----           | -----------                        |\n| Line           | The current line number            |\n| Text           | The current text                   |\n| State          | The current state                  |\n| RuleIndex      | The index of the rule that matched |\n\n\n## Parsing to a Non-Generic Result Set\nTo produce an untyped result set similar to the reference implementation of TextFSM.\n\n```csharp\nvar results = template.Run(string data)\n```\n\nThe results will be a list of rows that contain either a string value or an array of string values.\n\n## Parsing to a Dynamic Result Set\nTo produce an untyped dynamic result set.\n\n```csharp\nvar results = template.Run(string data).ToDynamic()\n```\n# Troubleshooting\n## Explain() Function\nThe explain function explains in detail each step of the match process and can help to understand why a template is not working as expected.\n\n```csharp\nvar template = Template.FromType\u003cMyTemplate\u003e().\n\nvar explain = template.Explain(data);\n```\n\n# Known Limitations\nIn general templates for the reference implementation of TextFSM should work with SharpTextFSM, and templates for SharpTextFSM should work with the reference implementation of TestFSM provided they do not use any extended features.\n\nKnown limitations include:\n* The .NET regular expression engine is subtly different from the TextFSM regular expression engine, which can create incompatibility between the two implementations.\n* Inline capture groups in 'Value' statements are not supported, for example, 'Value INLINE_CAPTURE_GROUP ((?P\\\u003cFirstGroup\\\u003eexpression) (?P\\\u003cSecondGroup\\\u003eexpression))'. In the reference implementation, a match would return a separate group inside of each existing 'Value' capture, in .NET regular expressions the syntax for a named capture group is different, and inline capture groups do not map cleanly to type mapping.","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbitvantage%2Fsharptextfsm","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbitvantage%2Fsharptextfsm","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbitvantage%2Fsharptextfsm/lists"}