Created
March 16, 2017 21:02
-
-
Save pcrama/a6ff54f759e1cc3d0ba3503184416d6c to your computer and use it in GitHub Desktop.
Simplistic command line interface for Windows Search
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System.Data.OleDb; | |
using System; | |
class WDSCli | |
{ | |
static void Main(string[] args) | |
{ | |
int maxResults = 200; // Don't output more than this amount of results | |
bool help = args.Length == 0; | |
string searchRoot = ""; // limit scope of search | |
string likeQuery = ""; | |
bool inQuery = false; | |
bool winPathSyntax = false; | |
bool verbose = false; | |
int i = 0; | |
while (i < args.Length) { | |
if (inQuery) { | |
likeQuery += ("" == likeQuery?"":"%") + args[i]; | |
} else if ("-h" == args[i] || "--help" == args[i]) { | |
help = true; | |
} else if ("-w" == args[i] || "--win" == args[i]) { | |
winPathSyntax = true; | |
} else if ("-v" == args[i] || "--verbose" == args[i]) { | |
verbose = true; | |
} else if ("-r" == args[i] || "--root" == args[i]) { | |
searchRoot = args[++i]; | |
} else if ("-m" == args[i] || "--max" == args[i]) { | |
maxResults = int.Parse(args[++i]); | |
} else if ("--" == args[i]) { | |
inQuery = true; | |
} else { | |
inQuery = true; | |
likeQuery = args[i]; | |
} | |
++i; | |
} | |
if (help || "" == likeQuery) { | |
usage(); | |
} else { | |
run(searchRoot, likeQuery, maxResults, winPathSyntax, verbose); | |
} | |
} | |
static void usage() | |
{ | |
Console.Error.WriteLine( | |
"wdscli [--max 200] # -m: limit result count\n" | |
+ " [--root <search root>] # -r: set root of search\n" | |
+ " [--verbose] # -v\n" | |
+ " [--win] # -w: query fragments and output use \\ as directory separator\n" | |
+ " [--help] # -h: this help text\n" | |
+ " [--] <query fragments>" | |
+ "\n" | |
+ "Query Windows File Search\n" | |
+ "\n" | |
+ "The query fragments are matched against the full file's path (search root\n" | |
+ "included) each of the fragments must appear in the given order. There is\n" | |
+ "an issue where a fragment 388 doesn't match 3880 but I don't have a\n" | |
+ "workaround for that.\n" | |
+ "\n" | |
+ "Special characters can be used (see SQL LIKE operator's syntax)\n" | |
+ "_ matches any character. I don't know how to escape it.\n" | |
+ "[ab] macthes 'a' or 'b'. Ranges are allowed, too.\n" | |
+ "[^ab] anything but 'a' or 'b'\n" | |
+ "% matches anything. This is inserted between fragments automatically\n" | |
+ "^ (only special as first char of first fragment) first fragment must\n" | |
+ " match at the start of the full path name\n" | |
+ "$ (only special as last char of last fragment) first fragment must\n" | |
+ " match at the end of the full path name\n" | |
); | |
} | |
static void run(string searchRoot, string likeQuery, int maxResults, bool winPathSyntax, bool verbose) | |
{ | |
if (!winPathSyntax) { | |
// translate unix path syntax to windows path syntax in query | |
likeQuery = likeQuery.Replace('/', '\\'); | |
} | |
if (likeQuery.StartsWith("^")) { | |
likeQuery = likeQuery.Substring(1, likeQuery.Length - 1); | |
} else { | |
likeQuery = "%" + likeQuery; | |
} | |
if (likeQuery.EndsWith("$")) { | |
likeQuery = likeQuery.Substring(0, likeQuery.Length - 1); | |
} else { | |
likeQuery = likeQuery + "%"; | |
} | |
/* I wanted to do this: | |
string query = @"SELECT System.ItemPathDisplay FROM SystemIndex WHERE " + | |
("" == searchRoot?"":@"scope = @searchRoot AND ") + | |
@"System.ItemPathDisplay LIKE @likeQuery"; | |
OleDbCommand command = new OleDbCommand(query, connection); | |
command.Parameters.AddWithValue("@searchRoot", searchRoot); | |
command.Parameters.AddWithValue("@likeQuery", likeQuery); | |
but I got this exception: | |
System.InvalidOperationException: The ICommandWithParameters interface is not supported by the 'Search.CollatorDSO' provider. Command parameters are unsupported with the current provider. | |
So I settled for this low-tech escaping mechanism: */ | |
likeQuery = likeQuery.Replace("'", "''"); | |
// MSDN suggests using ISearchCatalogManager::GetQueryHelper and its get_ConnectionString method | |
// but it didn't work for me | |
string connectionString = "Provider=Search.CollatorDSO;Extended Properties=\"Application=Windows\""; | |
OleDbConnection connection = new OleDbConnection(connectionString); | |
string query = @"SELECT TOP " + maxResults + @" System.ItemPathDisplay FROM SystemIndex " + | |
"WHERE " + | |
("" == searchRoot?"":@"scope ='file:" + searchRoot + "' AND ") + | |
@"System.ItemPathDisplay LIKE '" + likeQuery + "' " + | |
"AND NOT System.ItemName LIKE '%~' AND NOT System.ItemName LIKE '%#' " + | |
"ORDER BY System.ItemPathDisplay"; | |
if (verbose) { Console.Error.WriteLine(query); } | |
OleDbCommand command = new OleDbCommand(query, connection); | |
connection.Open(); | |
OleDbDataReader reader = command.ExecuteReader(); | |
while (reader.Read()) { | |
string row = reader.GetString(0); | |
Console.WriteLine(winPathSyntax?row:row.Replace('\\', '/')); | |
} | |
connection.Close(); | |
} | |
} | |
/* Local Variables: */ | |
/* mode: c */ | |
/* compile-command: "c:/Windows/Microsoft.NET/Framework64/v4.0.30319/csc.exe wdscli.cs" */ | |
/* End: */ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment