Search

I recently had the need to search a web directory’s application files for a particular string. Not as a customer-facing function, just as a one-off development need.

Well, CFEclipse (as truly awesome a tool as it is) doesn’t have a way to search multiple files for the same string simultaneously, and while Home Site does have this feature (”Extended Find / Extended Replace”), if you use it on anything but the smallest directories, it consistently times out and needs to be restarted again. So below is a little utility I wrote to recursively search a given directory for a given string. Its basically just two Java objects wrapped in a filtered CFDIRECTORY loop.

“But Tyler, why did you use Java when you could have just used CFFILE?” you say. Well, for me there’s now been several projects wherein CFFILE has considerably underperformed compared to its Java counterparts, and with the buffered reader object specifically, I can pass in fairly decent size files without ColdFusion choking on them. And its fast. Blazing fast. My browser almost caught fire (but I guess that’s the price you pay for efficiency). So nowadays, if I need a function that CFFILE would normally handle (at least in regards to reading and writing), I first consider the Java alternatives.

By the way, there is significant mixing of logic and presentation here, so be warned if you need to avert your eyes. Anyway, HTH.

<!--- the string to search for --->
<cfset request.searchstring = "thestring" />
<!--- the path to look in --->
<cfset request.startingpath = "/var/www" />
<!--- should the search be recursive. careful on this one. --->
<cfset request.recurse = false>
<cfset request.filecount = 0 />
<cfset request.linecount = 0 />
<cfset request.fileshown = 0 />    

<cfdirectory name="files"
    action="list"
    directory="#request.startingpath#"
    recurse="#request.recurse#"
    filter="*.cfm|*.cfc|*.cfml|*.js|*.css|*.htm|*.html"
    sort="directory asc, name asc" />    

<html>
<head>
    <style>
        * {
            font-size: 12px;
            font-family: 'courier new';
        }
        table {
            border: 0;
            padding: 0;
            border-collapse: collapse;
        }
        th { text-align: left; }
        th, td { padding: 0 15px 0 5px; }
        .even { background: #dadada; }
        .filename { font-weight: bold; }
    </style>
</head>    

<body>    

    <table>
        <tr>
            <th colspan="3">directory/filename</th>
        </tr>
        <tr>
            <th> </th>
            <th>num</th>
            <th>line of code</th>
        </tr>    

        <cfloop query="files">
            <!--- exclude this file from the search --->
            <cfif files.name NEQ listlast(cgi.SCRIPT_NAME,"/")>    

                <!--- set the source file to be read --->
                <cfset variables.sourcefile = files.directory & "\" & files.name />
                <cfset variables.linenumber = 1 />    

                <!--- instantiate the necessary java objects --->
                <cfset variables.filereader = createObject("java","java.io.FileReader").init(variables.sourcefile) />
                <cfset variables.bufferedreader = createObject("java","java.io.BufferedReader").init(variables.filereader) />    

                <!--- set the current line to the first available line in the file --->
                <cfset variables.currentline = variables.bufferedreader.readLine() />    

                <!--- beginning looping over the file line by line --->
                <cfloop condition="isDefined(' variables.currentline')">    

                    <!--- check the current line for the given search string --->
                    <cfif variables.currentline CONTAINS request.searchstring >    

                        <cfif request.fileshown EQ 0>
                            <!--- stack multiple results under a single file name --->
                            <tr valign="top" <cfif request.filecount MOD 2 EQ 0> class="even" </cfif> >
                                    <td colspan="3" class="filename"><cfoutput>#variables.sourcefile#</cfoutput></td>
                            </tr>   
                            <cfset request.fileshown = 1 />
                        </cfif>    

                        <!--- list the line number and line code containing the search string --->
                        <tr valign="top" <cfif request.filecount MOD 2 EQ 0> class="even" </cfif> >
                                <td> </td>
                                <cfoutput>
                                    <td align="right">#variables.linenumber#</td>
                                    <td>#htmleditformat(variables.currentline)#</td>
                                </cfoutput>
                        </tr>    

                        <cfset request.linecount = request.linecount + 1 />
                    </cfif>    

                    <!--- try to get the next line in the file. --->
                    <cfset variables.currentline = variables.bufferedreader.readLine() />
                    <cfset variables.linenumber = variables.linenumber + 1 />
                </cfloop>    

                <!--- close the buffered reader object --->
                <cfset variables.bufferedreader.close() />
                <cfif request.fileshown EQ 1>
                    <cfset request.fileshown = 0 />
                    <cfset request.filecount = request.filecount + 1>
                </cfif>
            </cfif>
        </cfloop>    

        <!--- list the line number and line code containing the search string --->
        <tr valign="top" <cfif request.filecount MOD 2 EQ 0> class="even" </cfif> >
            <th colspan="3">
                <cfoutput>
                    #request.linecount# matching lines in #request.filecount# files. #files.recordcount# files searched.
                </cfoutput>
            </th>
        </tr>
    </table>    

</body>
</html>

Comments are closed.