ismaeel ali
ismaeel ali

Reputation: 43

How to use findstr in powershell to search for a string in the whole drive?

So I received the task of finding a specific log file in a cyber security lab. I am meant to find the log file for a password tool which contains the information for a man called Alan Jones (username alan.jones).

I have been given the clue of using the findstr command, however, this is only useful for searching through one directory.

Is there a way to use findstr to get powershell to search through the whole drive for a specific string?

Upvotes: 1

Views: 14978

Answers (3)

mklement0
mklement0

Reputation: 437062

If speed matters, stick with findstr.exe, and use the /s switch to perform a recursive search, as vonPryz suggests:

findstr /s /m /c:alan.jones c:\*.log
  • /s makes findstr search recursively; that is, the input directory as well as as all its subdirectories and their subdirectories, ... are searched.

  • /c:... looks for the search string literally (default is to treat it as a regular expression, in which case . is a metacharacter (a character with special meanings) that matches any input character);

  • /m prints only the paths of the files in which the search string is found.

Note:

  • findstr.exe invariably includes hidden directories and files in the search.

  • findstr.exe quietly ignores inaccessible directories, i.e., directories that cannot be accessed due to lack of permissions.

Caveat: The string will only be found in files that use ASCII character encoding or one of its supersets (ANSI, OEM, UTF-8); to match all of the latter, the search string itself must comprise ASCII-range characters only; e.g, it mustn't contain accented letters such as é.


If you need to also recognize additional character encodings, notably UTF-16LE ("Unicode"), use Select-String in combination with Get-ChildItem -Recurse, but note that it will be noticeably slower.

On the plus side, Select-String:

  • supports more powerful regular expression (.NET's)
  • offers additional features
  • outputs objects that complement the lines found with rich metadata, notably what parts of the line matched; access the .Line property to extract the line only; .Path contains the full path of the input file.

By default, the search argument passed to -Pattern is interpreted as a regular expression, as with findstr. Add the -SimpleMatch switch to perform a literal string search instead, which not only avoids the need to \-escape special characters such as . in the search string, but also speeds up the operation.

As js2010, suggests, another way to speed up the command is to use the -Filter parameter (e.g., -Filter *.log) rather than including the wildcard pattern in the -Path parameter argument (-Path *.log).

If you need to include hidden directories and files, add the -Force switch; by default Get-ChildItem and Get-Item ignore them.

Given that you're searching the entire drive, you're likely to encounter directories you won't be able to access due to lack of permissions, especially if you don't run with elevation and also include -Force (but even with elevation (running as admin) access-denied errors can occur).

Use -ErrorAction SilentlyContinue to silence errors; if you need to inspect errors later, also add something like -ev errs (-ev is short for -ErrorVariable), which collects the errors that occur in variable $errs.

To put it all together:

# Running with elevation (as admin) lessens the risk of access-denied errors.
# Add -Force to include hidden dirs. and files.
Get-ChildItem -Recurse C:\ -Filter *.log -ErrorAction SilentlyContinue -ev errs | 
  Select-String -Pattern alan.jones -SimpleMatch

Finally, note that Select-String can itself target files, but only those in a single directory; also, hidden files are invariably ignored.

E.g., to only search the current directory's *.log files:

Select-String -Pattern alan.jones -SimpleMatch -Path *.log

Upvotes: 3

js2010
js2010

Reputation: 27418

I was trying to see if there was some nice parallel way to do it. Here's an interesting yet problematic script from Windows Powershell in Action. It makes a job for each path you give it. Unfortunately, relative paths don't work in jobs.

# Search-FilesInParallel.ps1

# relative paths in jobs are relative to ~\documents
# select-string -list only shows first match in file
# select-string in jobs outputs invisibly
# wait-job -any finishes if any job finishes
param (
  [parameter(mandatory=$true, position=0)]
  $Pattern,

  [parameter(mandatory=$true, position=1)]
  [string[]]
  $Path,

  [parameter()]
  $Filter = "*.txt",

  [parameter()]
  [switch]
  $Any
)
$jobid = [Guid]::NewGuid().ToString() 
$jobs = foreach ($element in $path)
{
  write-host "start-job -name Srch{$jobid}"  # added this
  Start-Job -name "Srch{$jobid}" -scriptblock {  # moved dollar sign to "$jobid"
    param($pattern, $path, $filter, $any)
    Get-ChildItem -Path $path -Recurse -Filter $filter | # added "|"
    Select-String -list:$any $pattern | select *  # added "| select *"
  } -ArgumentList $pattern,$element,$filter,$any
}
Wait-Job -any:$any $jobs | Receive-Job # -any: an unsuccessful job could finish 1st
Remove-Job -force $jobs

Compile an example list of folders:

 $list = ls -r \ -filter *.log -ea 0 | select -expand directoryname -ea 0 | sort -u

Run it with a pattern I know can be found:

.\Search-FilesInParallel.ps1 -path $list -filter *.log -pattern '09/01/2017 23:35:30'

Output without the exceptions:

IgnoreCase : True
LineNumber : 2
Line       : INFO:   09/01/2017 23:35:30
Filename   : DPINST.LOG
Path       : C:\Windows\DPINST.LOG
Pattern    : 09/01/2017 23:35:30
Context    :
Matches    : {09/01/2017 23:35:30}
RunspaceId : 4a9634be-7217-4225-8bcc-bfd326cf722e

IgnoreCase : True
LineNumber : 37
Line       : INFO:   09/01/2017 23:35:30
Filename   : DPINST.LOG
Path       : C:\Windows\DPINST.LOG
Pattern    : 09/01/2017 23:35:30
Context    :
Matches    : {09/01/2017 23:35:30}
RunspaceId : 4a9634be-7217-4225-8bcc-bfd326cf722e

IgnoreCase : True
LineNumber : 39
Line       : INFO:   09/01/2017 23:35:30
Filename   : DPINST.LOG
Path       : C:\Windows\DPINST.LOG
Pattern    : 09/01/2017 23:35:30
Context    :
Matches    : {09/01/2017 23:35:30}
RunspaceId : 4a9634be-7217-4225-8bcc-bfd326cf722e

Upvotes: 0

jfrmilner
jfrmilner

Reputation: 1768

If you're going to use PowerShell I'd recommend you stick with the native cmdlets over using findstr, Select-String can do what you want:

Get-ChildItem C:\*.log -Recurse | Select-String -Pattern "alan\.jones"

For more information on Select-String run Get-Help Select-String -Online

Upvotes: 3

Related Questions