(view source code of rxgrep.cs as plain text)
using System;
using System.Collections.Generic;
using System.IO;
using System.Text.RegularExpressions;
namespace RobvanderWoude
{
class RxGrep
{
static readonly string progver = "3.01";
#region Global Variables
static bool dedup = false;
static bool returngroup = false;
static bool singlelinemode = false;
static int skipmatches = 0;
static int takematches = 0;
static int groupnum = 0;
static int bytes = -1;
static List<string> dedupedmatches = new List<string>( );
#endregion Global Variables
static int Main( string[] args )
{
#region Initialize Variables
bool 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 Parsing
if ( 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 "/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":
if ( returngroup )
{
return ShowHelp( "Duplicate switch /G" );
}
returngroup = true;
try
{
groupnum = Convert.ToInt32( option.Substring( 3 ) );
if ( groupnum < 0 )
{
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 ) );
}
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 Validation
if ( Console.IsInputRedirected )
{
// Read the redirected Standard Input
input = Console.In.ReadToEnd( );
if ( bytes != -1 )
{
input = input.Substring( 0, Math.Min( bytes, input.Length ) );
}
}
else
{
// Check if the file name is valid
if ( filename.IndexOf( "/" ) > -1 )
{
return ShowHelp( );
}
if ( filename.IndexOfAny( "?*".ToCharArray( ) ) > -1 )
{
return ShowHelp( "Wildcards not allowed" );
}
// Check if the file exists
if ( File.Exists( filename ) )
{
// Read the file content
using ( 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 action
if ( 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 substrings
public static int DisplayMatches( string haystack, string needle, RegexOptions options )
{
int counter = 0;
int displayed = 0;
// Get all matches
MatchCollection 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 >= groupnum )
{
Group group = match.Groups[groupnum];
if ( !dedupedmatches.Contains( group.Value ) )
{
Console.WriteLine( group.Value );
dedupedmatches.Add( group.Value );
displayed += 1;
}
}
}
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 >= groupnum )
{
Group group = match.Groups[groupnum];
Console.WriteLine( group.Value );
displayed += 1;
}
else
{
Console.WriteLine( match.Value );
displayed += 1;
}
}
counter += 1;
}
}
}
return displayed;
}
#region Error Handling
public static int ShowHelp( params string[] errmsg )
{
#region Error Message
if ( 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: /D do not show Duplicate matches
/F:nn search only the First nn bytes
/G:nn return match Group #nn
/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.
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 Text
Console.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( "/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.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 Text
return -1;
}
#endregion Error Handling
}
}
page last modified: 2024-04-16; loaded in 0.0120 seconds