(view source code of rxgrep.cs as plain text)
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
namespace RobvanderWoude{ class RxGrep {static readonly string progver = "3.02";
#region Global Variablesstatic bool dedup = false;
static bool returngroup = false;
static bool singlelinemode = false;
static int skipmatches = 0;
static int takematches = 0;
static string groupseparator = " ";
static List<int> groupnums = new List<int>( );
static int bytes = -1;
static List<string> dedupedmatches = new List<string>( );
#endregion Global Variablesstatic int Main( string[] args )
{ #region Initialize Variablesbool caseset = false;
bool quiet = false;
bool skipset = false;
bool takeset = false;
string filename = string.Empty;
string pattern;
string input = string.Empty;
int matchcount;
int redirectnum = ( Console.IsInputRedirected ? 1 : 0 );
RegexOptions regexoptions = RegexOptions.None;
#endregion Initialize Variables #region Command Line Parsingif ( args.Length + redirectnum < 2 )
{return ShowHelp( );
}List<string> arguments = new List<string>( args );
if ( arguments.Contains( "/?" ) )
{return ShowHelp( );
}if ( !Console.IsInputRedirected )
{filename = arguments[0];
arguments.RemoveAt( 0 );
}pattern = arguments[0];
arguments.RemoveAt( 0 );
foreach ( string option in arguments )
{switch ( option.ToUpper( ).Substring( 0, 2 ) )
{case "/?":
return ShowHelp( );
case "/A":
if ( groupseparator == "\t" )
{return ShowHelp( "Duplicate switch /A" );
}groupseparator = "\t";
break;
case "/D":
if ( dedup )
{return ShowHelp( "Duplicate switch /D" );
}dedup = true;
break;
case "/F":
if ( bytes != -1 )
{return ShowHelp( "Duplicate switch /F" );
} try {bytes = Convert.ToInt32( option.Substring( 3 ) );
}catch ( Exception e )
{Console.Error.WriteLine( "Error: {0}", e.Message );
return ShowHelp( string.Format( "Invalid command line switch \"{0}\"", option ) );
}break;
case "/G":
returngroup = true;
int groupnum = 0;
try {groupnum = Convert.ToInt32( option.Substring( 3 ) );
if ( groupnum < 1 )
{return ShowHelp( "Invalid group number {0} specified", groupnum.ToString( ) );
} }catch ( Exception e )
{Console.Error.WriteLine( "Error: {0}", e.Message );
return ShowHelp( string.Format( "Invalid command line switch \"{0}\"", option ) );
}if ( groupnum > 0 )
{groupnums.Add( groupnum );
}break;
case "/I":
if ( caseset )
{return ShowHelp( "Duplicate switch /L" );
}regexoptions |= RegexOptions.IgnoreCase;
caseset = true;
break;
case "/L":
if ( singlelinemode )
{return ShowHelp( "Duplicate switch /I" );
}singlelinemode = true;
break;
case "/Q":
if ( quiet )
{return ShowHelp( "Duplicate switch /Q" );
}quiet = true;
break;
case "/S":
if ( skipset )
{return ShowHelp( "Duplicate switch /S" );
} try {skipmatches = Convert.ToInt32( option.Substring( 3 ) );
}catch ( Exception e )
{Console.Error.WriteLine( "Error: {0}", e.Message );
return ShowHelp( string.Format( "Invalid command line switch \"{0}\"", option ) );
}break;
case "/T":
if ( takeset )
{return ShowHelp( "Duplicate switch /T" );
} try {takematches = Convert.ToInt32( option.Substring( 3 ) );
}catch ( Exception e )
{Console.Error.WriteLine( "Error: {0}", e.Message );
return ShowHelp( string.Format( "Invalid command line switch \"{0}\"", option ) );
}break;
default:
return ShowHelp( string.Format( "Invalid command line {0}: \"{1}\"", ( option[0] == '/' ? "switch" : "argument" ), option ) );
} } #endregion Command Line Parsing #region Command Line Arguments Validationif ( Console.IsInputRedirected )
{ // Read the redirected Standard Inputinput = Console.In.ReadToEnd( );
if ( bytes != -1 )
{input = input.Substring( 0, Math.Min( bytes, input.Length ) );
} } else { // Check if the file name is validif ( filename.IndexOf( "/" ) > -1 )
{return ShowHelp( );
}if ( filename.IndexOfAny( "?*".ToCharArray( ) ) > -1 )
{return ShowHelp( "Wildcards not allowed" );
} // Check if the file existsif ( File.Exists( filename ) )
{ // Read the file contentusing ( StreamReader file = new StreamReader( filename ) )
{if ( bytes == -1 )
{input = file.ReadToEnd( );
} else {char[] buffer = new char[bytes];
file.Read( buffer, 0, bytes );
input = string.Join( string.Empty, buffer );
} } } else {return ShowHelp( string.Format( "File not found: \"{0}\"", filename ) );
} }if ( dedup && ( skipset || takeset ) )
{return ShowHelp( "Switch /D cannot be combined with switches /S or /T" );
} #endregion Command Line Arguments Validation // Now that the command line parsing is finally done, let's get some actionif ( singlelinemode )
{matchcount = 0;
foreach ( string line in input.Split( new char[] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries ) )
{matchcount += DisplayMatches( line, pattern, regexoptions );
} } else {matchcount = DisplayMatches( input, pattern, regexoptions );
}return matchcount;
} // The main functionality: display all matching substringspublic static int DisplayMatches( string haystack, string needle, RegexOptions options )
{int counter = 0;
int displayed = 0;
// Get all matchesMatchCollection matches = Regex.Matches( haystack, needle, options );
if ( dedup )
{ //List<string> dedupedmatches = new List<string>( );foreach ( Match match in matches )
{if ( returngroup )
{if ( match.Groups.Count >= groupnums.Count )
{bool empty = true;
foreach ( int groupnum in groupnums )
{Group group = match.Groups[groupnum];
if ( !dedupedmatches.Contains( group.Value ) )
{if ( empty )
{Console.Write( group.Value );
} else {Console.Write( groupseparator + group.Value );
}dedupedmatches.Add( group.Value );
displayed += 1;
empty = false;
} }Console.WriteLine( );
} }else if ( !dedupedmatches.Contains( match.Value ) )
{Console.WriteLine( match.Value );
dedupedmatches.Add( match.Value );
displayed += 1;
} } } else {if ( matches.Count > skipmatches )
{foreach ( Match match in matches )
{if ( counter >= skipmatches && ( displayed < takematches || takematches == 0 ) )
{if ( returngroup && match.Groups.Count >= groupnums.Count )
{bool empty = true;
foreach ( int groupnum in groupnums )
{Group group = match.Groups[groupnum];
if ( empty )
{Console.Write( group.Value );
} else {Console.Write( groupseparator + group.Value );
}displayed += 1;
empty = false;
}Console.WriteLine( );
} else {Console.WriteLine( match.Value );
displayed += 1;
} }counter += 1;
} } }return displayed;
} #region Error Handlingpublic static int ShowHelp( params string[] errmsg )
{ #region Error Messageif ( errmsg.Length > 0 )
{List<string> errargs = new List<string>( errmsg );
errargs.RemoveAt( 0 );
Console.Error.WriteLine( );
Console.ForegroundColor = ConsoleColor.Red;
Console.Error.Write( "ERROR:\t" );
Console.ForegroundColor = ConsoleColor.White;
Console.Error.WriteLine( errmsg[0], errargs.ToArray( ) );
Console.ResetColor( );
} #endregion Error Message #region Help Text /* RxGrep, Version 3.00 Multi-line FindStr/Grep like tool Usage: RXGREP filename pattern [ options ] or: command | RXGREP pattern [ options ] Where: filename is the file to be filtered command is the command whose standard output is to be filtered pattern is the search pattern (regular expression) Options: /A use tAb as separator for multiple groups (default: space) /D do not show Duplicate matches /F:nn search only the First nn bytes /G:nn return match Group #nn (may be used multiple times) /I case Insensitive search /L single Line mode: ^ and $ match begin and end of each line /Q Quiet mode: no message if no match is found /S:nn Skip the first nn matches /T:nn Take only nn matches Example: ROBOCOPY D:\sourcedir E:\targetdir /NP /MIR | RXGREP "\s+\d+\s+D:\\sourcedir\\[^\n\r]*\r\n([^\n\r\\]+\r\n)+" (to be read as a single command line) will return something like: 125 D:\sourcedir\subdir\ New File 342 brandnewfile.ext Newer 4.06m updatedfile.ext *EXTRA File 2.40m deletedfile.ext Notes: If /F:nn is used and a file is specified, only the first nn bytes of that file will be read; if the input is redirected, it is read entirely, and then chopped to nn bytes before being searched. /G:nn can be used multiple times; all matching groups are written to screen, separated by spaces, unless /A is used, in which case the matches will be separated by tabs; matching groups are shown in order of input, not in order of command line arguments, e.g. /G:1 /G:3 will show the same result as /G:3 /G:1. Switches /D and /G cannot be combined with /S or /T. Return code ("errorlevel") will equal the number of matches found, or -1 in case of (command line) errors. Written by Rob van der Woude https://www.robvanderwoude.com */ #endregion Help Text #region Display Help TextConsole.Error.WriteLine( );
Console.Error.WriteLine( "RxGrep, Version {0}", progver );
Console.Error.WriteLine( "Multi-line FindStr/Grep like tool" );
Console.Error.WriteLine( );
Console.Error.Write( "Usage: " );
Console.ForegroundColor = ConsoleColor.White;
Console.Error.WriteLine( "RXGREP filename pattern [ options ]" );
Console.ResetColor( );
Console.Error.Write( "or: " );
Console.ForegroundColor = ConsoleColor.White;
Console.Error.WriteLine( "command | RXGREP pattern [ options ]" );
Console.ResetColor( );
Console.Error.WriteLine( );
Console.Error.Write( "Where: " );
Console.ForegroundColor = ConsoleColor.White;
Console.Error.Write( "filename" );
Console.ResetColor( );
Console.Error.WriteLine( " is the file to be filtered" );
Console.ForegroundColor = ConsoleColor.White;
Console.Error.Write( " command" );
Console.ResetColor( );
Console.Error.WriteLine( " is the command whose standard output is to be filtered" );
Console.ForegroundColor = ConsoleColor.White;
Console.Error.Write( " pattern" );
Console.ResetColor( );
Console.Error.WriteLine( " is the search pattern (regular expression)" );
Console.Error.WriteLine( );
Console.Error.Write( "Options: " );
Console.ForegroundColor = ConsoleColor.White;
Console.Error.Write( "/A" );
Console.ResetColor( );
Console.Error.Write( " use t" );
Console.ForegroundColor = ConsoleColor.White;
Console.Error.Write( "A" );
Console.ResetColor( );
Console.Error.WriteLine( "b as separator for multiple groups" );
Console.Error.WriteLine( " (default: space)" );
Console.ForegroundColor = ConsoleColor.White;
Console.Error.Write( " /D" );
Console.ResetColor( );
Console.Error.Write( " do not show " );
Console.ForegroundColor = ConsoleColor.White;
Console.Error.Write( "D" );
Console.ResetColor( );
Console.Error.WriteLine( "uplicate matches" );
Console.ForegroundColor = ConsoleColor.White;
Console.Error.Write( " /F:nn" );
Console.ResetColor( );
Console.Error.Write( " search only the " );
Console.ForegroundColor = ConsoleColor.White;
Console.Error.Write( "F" );
Console.ResetColor( );
Console.Error.Write( "irst " );
Console.ForegroundColor = ConsoleColor.White;
Console.Error.Write( "nn" );
Console.ResetColor( );
Console.Error.WriteLine( " bytes" );
Console.ForegroundColor = ConsoleColor.White;
Console.Error.Write( " /G:nn" );
Console.ResetColor( );
Console.Error.Write( " return match " );
Console.ForegroundColor = ConsoleColor .White;
Console.Error.Write( "G" );
Console.ResetColor( );
Console.Error.Write( "roup " );
Console.ForegroundColor = ConsoleColor.White;
Console.Error.WriteLine( "#nn" );
Console.ResetColor( );
Console.ForegroundColor = ConsoleColor.White;
Console.Error.Write( " /I" );
Console.ResetColor( );
Console.Error.Write( " case " );
Console.ForegroundColor = ConsoleColor.White;
Console.Error.Write( "I" );
Console.ResetColor( );
Console.Error.WriteLine( "nsensitive search" );
Console.ForegroundColor = ConsoleColor.White;
Console.Error.Write( " /L" );
Console.ResetColor( );
Console.Error.Write( " single " );
Console.ForegroundColor = ConsoleColor.White;
Console.Error.Write( "L" );
Console.ResetColor( );
Console.Error.WriteLine( "ine mode: ^ and $ match begin and end of each line" );
Console.ForegroundColor = ConsoleColor.White;
Console.Error.Write( " /Q Q" );
Console.ResetColor( );
Console.Error.WriteLine( "uiet mode: no message if no match is found" );
Console.ForegroundColor = ConsoleColor.White;
Console.Error.Write( " /S:nn S" );
Console.ResetColor( );
Console.Error.Write( "kip the first " );
Console.ForegroundColor = ConsoleColor.White;
Console.Error.Write( "nn" );
Console.ResetColor( );
Console.Error.WriteLine( " matches" );
Console.ForegroundColor = ConsoleColor.White;
Console.Error.Write( " /T:nn T" );
Console.ResetColor( );
Console.Error.Write( "ake only " );
Console.ForegroundColor = ConsoleColor.White;
Console.Error.Write( "nn" );
Console.ResetColor( );
Console.Error.WriteLine( " matches" );
Console.Error.WriteLine( );
Console.Error.Write( "Example: " );
Console.ForegroundColor = ConsoleColor.White;
Console.Error.WriteLine( @"ROBOCOPY D:\sourcedir E:\targetdir /NP /MIR |" );
Console.Error.WriteLine( @" RXGREP ""\s+\d+\s+D:\\sourcedir\\[^\n\r]*\r\n([^\n\r\\]+\r\n)+""" );
Console.ResetColor( );
Console.Error.WriteLine( " (to be read as a single command line) will return something like:" );
Console.ForegroundColor = ConsoleColor.White;
Console.Error.WriteLine( @" 125 D:\sourcedir\subdir\" );
Console.Error.WriteLine( " New File 342 brandnewfile.ext" );
Console.Error.WriteLine( " Newer 4.06m updatedfile.ext" );
Console.Error.WriteLine( " *EXTRA File 2.40m deletedfile.ext" );
Console.ResetColor( );
Console.Error.WriteLine( );
Console.Error.Write( "Notes: If " );
Console.ForegroundColor = ConsoleColor.White;
Console.Error.Write( "/F:nn" );
Console.ResetColor( );
Console.Error.Write( " is used and a file is specified, only the first " );
Console.ForegroundColor = ConsoleColor.White;
Console.Error.Write( "nn" );
Console.ResetColor( );
Console.Error.WriteLine( " bytes" );
Console.Error.WriteLine( " of that file will be read; if the input is redirected, it is read" );
Console.Error.Write( " entirely, and then chopped to " );
Console.ForegroundColor = ConsoleColor.White;
Console.Error.Write( "nn" );
Console.ResetColor( );
Console.Error.WriteLine( " bytes before being searched." );
Console.ForegroundColor = ConsoleColor.White;
Console.Error.Write( " /G:nn" );
Console.ResetColor( );
Console.Error.WriteLine( " can be used multiple times; all matching groups are written" );
Console.Error.Write( " to screen, separated by spaces, unless " );
Console.ForegroundColor = ConsoleColor.White;
Console.Error.Write( "/A" );
Console.ResetColor( );
Console.Error.WriteLine( " is used, in which case" );
Console.Error.WriteLine( " the matches will be separated by tabs; matching groups are shown" );
Console.Error.WriteLine( " in order of input, not in order of command line arguments, e.g." );
Console.ForegroundColor = ConsoleColor.White;
Console.Error.Write( " /G:1 /G:3" );
Console.ResetColor( );
Console.Error.Write( " will show the same result as " );
Console.ForegroundColor = ConsoleColor.White;
Console.Error.Write( "/G:3 /G:1" );
Console.ResetColor( );
Console.Error.WriteLine( "." );
Console.Error.Write( " Switches " );
Console.ForegroundColor = ConsoleColor.White;
Console.Error.Write( "/D" );
Console.ResetColor( );
Console.Error.Write( " and " );
Console.ForegroundColor = ConsoleColor.White;
Console.Error.Write( "/G" );
Console.ResetColor( );
Console.Error.Write( " cannot be combined with " );
Console.ForegroundColor = ConsoleColor.White;
Console.Error.Write( "/S" );
Console.ResetColor( );
Console.Error.Write( " or " );
Console.ForegroundColor = ConsoleColor.White;
Console.Error.Write( "/T" );
Console.ResetColor( );
Console.Error.WriteLine( "." );
Console.Error.WriteLine( " Return code (\"errorlevel\") will equal the number of matches found," );
Console.Error.WriteLine( " or -1 in case of (command line) errors." );
Console.Error.WriteLine( );
Console.Error.WriteLine( "Written by Rob van der Woude" );
Console.Error.WriteLine( "https://www.robvanderwoude.com" );
#endregion Display Help Textreturn -1;
} #endregion Error Handling }}page last modified: 2025-10-11; loaded in 0.0131 seconds