Searching Google Desktop from PowerShell
I just noticed that Sean McLeod created a nice cmdlet for PowerShell that lets you query Windows Desktop Search from the PowerShell Command Line. I used to use Windows Desktop Search but got tired of it hanging and switched to, and settled on, for now, Google Desktop Search and I've been very happy with it.
After reading this I said, I'd like to created a cmdlet for Google Desktop Search, but who has the time? (Maybe I'll do it later)
I figured I could do something quick and dirty though. I took a look at the Google Desktop API Developer's Guide and saw some JavaScript samples for their psycho and obscure COM API. (Google does nearly everything in C++. Even their .NET sample code smells like C++.)
I did this at the PowerShell command line, as a client of the Google Desktop Search has to "register" itself, and I got stuck:
PS>$registrar = new-object -com "GoogleDesktop.Registrar"
PS>$regArray = "Title","Searching GDS from PowerShell","Descripton","For Fun","Icon","My Icon@1"
PS>$regId = [System.Guid]::NewGuid().ToString("B").ToUpper()
PS>$registrar.StartComponentRegistration($regId,$regArray)
Exception calling "StartComponentRegistration" with "2" argument(s): "The component description must contain a SAFEARRAY of six or eight VARIANTs" At line:1 char:38 + $registrar.StartComponentRegistration( <<<< $regId,$regArray)
...and they lost me at SAFEARRAY. I don't know why this little .NET array didn't get marshalled correctly. Probably because it's marshalled as a SAFEARRAY of BSTRs instead. Anyway, too hard, patience waning.
But, hey, GDS runs a local web server, right? So I should be able to query it locally. The docs say:
The search query URL, including your security token, is stored in the registry at:
HKEY_CURRENT_USER\Software\Google\Google Desktop\API\search_url
To use the example above, the stored query URL would be something like:
http://127.0.0.1:4664/search&s=1ftR7c_hVZKYvuYS-RWnFHk91Z0?q=
So from PowerShell:
PS>(Get-Item "HKCU:\Software\Google\Google Desktop\api").GetValue("search_url")
http://127.0.0.1:4664/search&s=1ftR7c_hVZKYvuYS-RWnFHk91Z0?q=
Cool. Then I can add the query after the "q=" and "&format=xml" to get something that PowerShell can sink its teeth into. I'll need to UrlEncode the query.
UPDATE: Note the num= and flags= at the end of the queryString. That indicates that we are only interested in files and we'll take as many as 1000.
PS>$query = "PowerShell"
PS>$searchUrl = (Get-Item "HKCU:\Software\Google\Google Desktop\api").GetValue("search_url")
PS>[System.Reflection.Assembly]::LoadWithPartialName("System.Web") > $null
PS>$newQuery = $searchUrl + [System.Web.HttpUtility]::UrlEncode($query) + "&format=xml&flags=576&num=1000"
PS>$webclient = new-object System.Net.WebClient
PS>$resultsXml = [xml]($webclient.DownloadString($newQuery))
PS>$resultsXml.results.result | select title, snippet, categorytitle snippet category
----- ------- --------
C:\Temp\fusionlogs\Defa... NET\Framework\v2.0.5072... file
Windows <b>PowerShell</... Windows <b>PowerShell</... web
del.icio.us/shanselman del.icio.us/shanselman ... web
C:\Temp\fusionlogs\Defa... NET\Framework\v2.0.5072... file
Windows Desktop Search ... Windows Desktop Search ... web
User Profile: Greg Borota User Profile: Greg Boro... web
del.icio.us/shanselman del.icio.us/shanselman ... web
Microsoft Survey  Microsoft Survey Th... web
Discussions in Windows ... Discussions in Windows ... web
Search Results: <b>powe... Search Results: <b>powe... web
Cool. So now I want just the files, and I want them to be actual FileInfo objects.
PS> $resultsXml.results.result | where { $_.category -eq "file"} | foreach-object { get-item $_.url }
Directory: Microsoft.PowerShell.Core\FileSystem:: C:\Temp\fusionlogs\Default\powershell.exe
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a--- 7/2/2006 3:08 AM 1625 System.Web.HTM
-a--- 7/2/2006 2:36 AM 1695 System.Windows.Forms.HTM...etc...
Now I'll save the whole file in my path in a file called "search-googledesktop.ps1" and change the first line to "$query = $args" to take command line arguments.
Since the objects that are coming out of this script are real FileInfo objects retrieved with the call to get-item in the final line, I can use my script in a larger pipeline like this:
.\search-googledesktop.ps1 "powershell" | get-content
or
.\search-googledesktop.ps1 "powershell" | get-location
or delete any file that has my personal information like a social security number in it.
.\search-googledesktop.ps1 "123" | remove-item -whatif
Seriously, once you have an indexer on your system, search for your Social Security Number. You'd be amazed at the old Excel sheets and crap that number gets into.
Anyway, cool. Sure beats their crap COM API.
File Attachment: search-googledesktop.ps1 (474 bytes)
About Scott
Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.
About Newsletter
Comments are closed.
Ahh, a mere mortal like the rest of us. I feel less ashamed that I can't finish my reading list now! Credibility is building once again!
Have a great holiday with the family!
Thanks Scott!