I was on Firefox today and wanted to search for a bookmark. I found the bookmark easily, but unlike Chrome Firefox has no way of showing which folder the bookmark is present in. So I created a PowerShell script to do just that. Mainly because I wanted some excuse to code in PowerShell and also because it felt like an interesting problem.
PowerShell can’t directly control Firefox as it doesn’t have a COM interface (unlike IE). So you’ll have to manually export the bookmarks. Good for us the export is into a JSON file, and PowerShell can read JSON files natively (since version 3.0 I think). The ConvertFrom-JSON cmdlet is your friend here. So export bookmarks and read them into a variable:
1 |
$bookmarks = Get-Content \path\to\bookmarks-2015-03-11.json | ConvertFrom-Json |
This gives me a tree structure of bookmarks.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
PS> $bookmarks guid : root________ title : index : 0 dateAdded : 1422217616861000 lastModified : 1423076331270000 id : 1 type : text/x-moz-place-container root : placesRoot children : {@{guid=menu________; title=Bookmarks Menu; index=0; dateAdded=1413121004000000; lastModified=1426013312897000; id=2; type=text/x-moz-place-container; root=bookmarksMenuFolder; children=System.Object[]}, @{guid=toolbar_____; title=Bookmarks Toolbar; index=1; dateAdded=1413121004000000; lastModified=1426014543697000; id=3; type=text/x-moz-place-container; root=toolbarFolder; children=System.Object[]}, @{guid=unfiled_____; title=Unsorted Bookmarks; index=3; dateAdded=1357715408000000; lastModified=1426044593123000; id=5; type=text/x-moz-place-container; root=unfiledBookmarksFolder}} |
Notice the children
property. It is an array of further objects – links to the first-level folders, basically. Each of these in turn have links to the second-level folders and so on.
The type
property is a good way of identifying if a node is a folder or a bookmark. If it’s text/x-moz-place-container
then it’s a folder. If it’s text/x-moz-place
then it’s a bookmark. (Alternatively one could also test whether the children
property is $null
).
So how do we go through this tree and search each node? Initially I was going to iteratively do it by going to each node. Then I remembered recursion (see! no exercise like this goes wasted! I had forgotten about recursion). So that’s easy then.
- Make a function. Pass it the bookmarks object.
- Said function looks at the object passed to it.
- If it’s a bookmark it searches the title and lets us know if it’s a match.
- If it’s a folder, the function calls itself with the next level folder as input.
Here’s the function:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
function Search-FxBookmarks { param($Bookmarks, [string]$PathSoFar="", [string]$SearchString) $NodeName = $Bookmarks.title $NewPath = "$PathSoFar\$NodeName" if ($Bookmarks.type -eq "text/x-moz-place-container") { # We are on a non-leaf node # Check if the non-leaf node name itself matches the search string if ($NodeName -match $SearchString) { Write-Host -ForegroundColor Cyan "$PathSoFar\$NodeName" } # Then call ourselves recursively for each of the children foreach ($Bookmark in $Bookmarks.children) { Search-FxBookmarks -Bookmarks $Bookmark -PathSoFar $NewPath -SearchString $SearchString } } if ($Bookmarks.type -eq "text/x-moz-place") { # We are on a leaf node, search if the title matches the search string if ($NodeName -match $SearchString) { Write-Host -ForegroundColor Green "$PathSoFar\$NodeName" } } } |
I decided to search folder names too. And just to distinguish between folder names and bookmark names, I use different colors.
Call the function thus (after exporting & reading the bookmarks into a variable):
1 |
Search-FxBookmarks -Bookmarks $bookmarks -SearchString "whatever" |
Here’s a screenshot of the output: