#!/usr/bin/perl # # spotlightls: a `ls' replacement that will automatically expand # any 'foo.savedSearch' parameters into the list of files produced # by that Spotlight search # # e.g. # [mithras] ls -l NewPDFs.savedSearch # -rw------- 1 mithras wheel 9002 Apr 21 11:32 /Users/mithras/.Trash/Web Receipts/Untitled.pdf # -rw-r--r-- 1 mithras wheel 225031 Apr 22 12:28 /Users/mithras/Documents/alon_liebler_199.pdf # # by Mithras The Prophet (mithras.the.prophet at that gmail thing) # # v.1.0.1 - 4/24/2005, used fork to pass query to mdfind without shell interfering # v.1.0.0 - 4/24/2005 # use warnings; use strict; my @newargs; for (my $i=0; $i < scalar @ARGV; $i++) { # if this is a .savedSearch, get the results! if ($ARGV[$i] =~ /\.savedSearch$/) { push(@newargs, execute_savedSearch($ARGV[$i])); } else { # otherwise, just add this directly to our arglist push(@newargs, $ARGV[$i]); } } # if we didn't get any results from saved searches, # we won't run `ls', (otherwise it will do a `ls .', which we don't want) # # but note that if we were passed no parameters at all, # we do indeed want to run a plain `ls' with no parameters # if ((scalar @ARGV == 0) || scalar @newargs > 0) { exec("ls",@newargs); } sub execute_savedSearch { my @results; while (scalar @_ > 0) { my ($thisarg) = shift (@_); # print STDERR "Expanding saved search: $thisarg\n"; # parse the searchfile my $query = ""; my @searchLocations; open (INFILE, "$thisarg") or die("Can't read saved search: '$thisarg'\n"); my $query_next = 0; my $searchlocation_next = 0; while () { if (m/RawQuery<\/key>/) { $query_next = 1; } elsif (m/FXScopeArrayOfPaths<\/key>/) { $searchlocation_next = 1; } elsif (m/<\/array>/) { $query_next = 0; $searchlocation_next = 0; } elsif ( $query_next && m/([^<]+)<\/string>/ ) { $query = $1; $query_next = 0; $query =~ s/&/&/g; $query =~ s/<//g; } elsif ( $searchlocation_next && m/([^<]+)<\/string>/ ) { my $thisloc = $1; $thisloc =~ s/&/&/g; $thisloc =~ s/<//g; $thisloc =~ s/kMDQueryScopeHome/$ENV{HOME}/ge; $thisloc =~ s/kMDQueryScopeComputer/\//g; push (@searchLocations, $thisloc); } } close (INFILE); # now, run and mdfind over each of the search locations: # print STDERR "\tquery: $query\n"; foreach my $searchLoc (@searchLocations) { # print STDERR "\tlocation: $searchLoc\n"; # we'll fork, and run mdfind # so we can pass the query parameter to mdfind without # the shell interpreter interfering my $pid = open(KID_TO_READ, "-|"); if ($pid) { # parent while () { # add each result to our final results array chomp; push (@results, $_); } close(KID_TO_READ) || warn "kid exited $?"; } else { # child exec("mdfind", '-onlyin',$searchLoc,$query) || die "can't exec program: $!"; # NOTREACHED } } shift; } return @results; }