PowerShell for Windows Admins


January 9, 2013  11:57 AM

Select-String – finding the first and last matches

Richard Siddaway Richard Siddaway Profile: Richard Siddaway

Today’s question concerns finding the first and last matches in a file

Sometimes, I need to make two passes at seeking content in this file, once for the first occurrence; and a second grep for obtaining the last occurrence of a phrase. After the second pass, I figure placing the values into an array is the best way, then need to combine first and last values onto one output line {somewhere else}.

Let’s consider the file we used in the first article in the series – http://msmvps.com/blogs/richardsiddaway/archive/2013/01/07/select-string-scenarios-fixed-columns.aspx

The file looks like this

12345ABCD123451234512345
1234512345ABCD1234512345
12345ABCD123451234512345
12345abcd123451234512345
123451234512345ABCD12345
12345ABCD123451234512345
123451234512345ABCD12345
12345123451234512345ABCD
1234512345ABCD1234512345

If you this select-string

Select-String -Path c:\test\*.txt -Pattern "\A\w{5}ABCD

you will get multiple matches

C:\test\fixedcol.txt:1:12345ABCD123451234512345
C:\test\fixedcol.txt:3:12345ABCD123451234512345
C:\test\fixedcol.txt:4:12345abcd123451234512345
C:\test\fixedcol.txt:6:12345ABCD123451234512345

So, how can we find the first and last matches – preferably in one pass.

I think the easiest way is to use the trick from the last article

$finds = Select-String -Path c:\test\*.txt -Pattern "\A\w{5}ABCD"

$finds[0]
$finds[-1]

The $finds variable conatins a collection of the MatchInfo objects created by Select-String.  The first match will always have the index of 0 and the last can always be referenecd by an index of -1. This information is returned:

C:\test\fixedcol.txt:1:12345ABCD123451234512345
C:\test\fixedcol.txt:6:12345ABCD123451234512345

If you want this in an object for further processing – try something like this

Get-ChildItem -Path c:\test -Filter *.txt -Recurse |
foreach {

$finds = $null
$finds = Select-String -Path $_.Fullname -Pattern "\A\w{5}ABCD"

if ($finds){
 
  $props = [ordered]@{
    Filename  = $finds[0].Path
    FirstLine = $finds[0].LineNumber
    FirstData = $finds[0].Line
    LastLine = $finds[-1].LineNumber
    LastData = $finds[-1].Line
  }
  New-Object -TypeName PSObject -Property $props
}
}

Use Get-ChildItem to find the files. For each of them run Select-String with your pattern. If you get any matches create an object holding the file path and your required properties. In this case I’m taking the first and last line numbers with the match data.

If you only have a single match in a file you will get the same data in the First* and Last* properties as the first and last match are the  same.  You could put another if statement to control this so the Last* properties aren’t populated if you want. 

January 9, 2013  11:18 AM

Workflow article 3

Richard Siddaway Richard Siddaway Profile: Richard Siddaway

 

The next in the series of articles on PowerShell workflows that are appearing on the Scripting Guy blog has been published.

The articles in the series that have been published are:

http://blogs.technet.com/b/heyscriptingguy/archive/2012/12/26/powershell-workflows-the-basics.aspx
http://blogs.technet.com/b/heyscriptingguy/archive/2013/01/02/powershell-workflows-restrictions.aspx
http://blogs.technet.com/b/heyscriptingguy/archive/2013/01/09/powershell-workflows-nesting.aspx

Look for the next article in one weeks time.

Until then Enjoy!


January 8, 2013  3:32 PM

Select-String–information on matching files

Richard Siddaway Richard Siddaway Profile: Richard Siddaway

Following on from yesterday’s post this is the second question:

Since I’m recursively searching thru files to find matching phrases, how can I obtain other directory service information about the matching files file(s) – this is more of a methodology technique question because I realize there are multiple ways of achieving this?

You could do something like this

foreach ($find in Select-String -Path c:\test\*.txt -Pattern "\A\w{5}ABCD" -List){
Get-ChildItem -Path $find.Path
}

Run the Select-String as before but only get the first match in each file.  Use foreach to access the match information and use the Path property to feed into Get-ChildItem.

If you want things to be a bit simpler – break it down to:

$finds = Select-String -Path c:\test\*.txt -Pattern "\A\w{5}ABCD" -List
foreach ($find in $finds){
Get-ChildItem -Path $find.Path
}

Alternatively if you want the PowerShell one-liner approach try

Get-ChildItem -Path  (Select-String -Path c:\test\*.txt -Pattern "\A\w{5}ABCD" -List).Path

Personally I would probably go for the simple approach


January 7, 2013  4:41 PM

Select-String scenarios – fixed columns

Richard Siddaway Richard Siddaway Profile: Richard Siddaway

 

I had some questions come in after mu recent post regarding select-string. I’ll answer them as a series of posts. First off:

I’m recursively searching thru many files, and want to pull out specific data in ‘fixed column’ positions from the line(s) that match the phrase I’m seeking, i.e. position 10 thru 15 of the line or  position 6 thru the end of the line (which might be unknown).
What is your preferred method for handling this situation?

I started by creating a file

12345ABCD123451234512345
1234512345ABCD1234512345
12345ABCD123451234512345
12345abcd123451234512345
123451234512345ABCD12345
12345ABCD123451234512345
123451234512345ABCD12345
12345123451234512345ABCD
1234512345ABCD1234512345

I want to pick out the string ABCD but ONLY when its in columns6-9.  A quick inspection shows I should get four lines returned.

If you go with a simple match you get all lines returned

PS> Select-String -Path c:\test\*.txt -Pattern “ABCD” -SimpleMatch

C:\test\fxedcol.txt:1:12345ABCD123451234512345
C:\test\fxedcol.txt:2:1234512345ABCD1234512345
C:\test\fxedcol.txt:3:12345ABCD123451234512345
C:\test\fxedcol.txt:4:12345abcd123451234512345
C:\test\fxedcol.txt:5:123451234512345ABCD12345
C:\test\fxedcol.txt:6:12345ABCD123451234512345
C:\test\fxedcol.txt:7:123451234512345ABCD12345
C:\test\fxedcol.txt:8:12345123451234512345ABCD
C:\test\fxedcol.txt:9:1234512345ABCD1234512345

Notice the match is case INSENSITIVE

This means we get into the world of regular expressions – joy!

This will work

Select-String -Path c:\test\*.txt -Pattern “\A.{5}ABCD”

The regular expression means match any 5 characters followed by ABCD starting at the beginning of the string.

Alternatively you could use

Select-String -Path c:\test\*.txt -Pattern “\A\w{5}ABCD”

This is the same except its accepting any word character (letter, digit, math symbol and punctuation)

These two searches are case INSENSITIVE

if you need case sensitivity then compare

Select-String -Path c:\test\*.txt -Pattern “\A\w{5}ABCD”  -CaseSensitive
Select-String -Path c:\test\*.txt -Pattern “\A\w{5}abcd”  -CaseSensitive

or

Select-String -Path c:\test\*.txt -Pattern “\A.{5}ABCD” -CaseSensitive
Select-String -Path c:\test\*.txt -Pattern “\A.{5}abcd” -CaseSensitive


January 5, 2013  7:10 AM

Select-String confusion

Richard Siddaway Richard Siddaway Profile: Richard Siddaway

I have seen a lot of confusion recently over the use of Select-String.

One mis-conception is that you need to use Get-Content to pipe the file contents into Select-String.  Not so. Select-String will read the file for you.

If you just want to scan the files in a single folder to find a specific string then Select-String can do the work for you

Select-String -Path C:\Test\*.txt -Pattern "trial" –SimpleMatch

If you need to work through a folder structure add get-ChildItem to the pipeline

Get-ChildItem -Path C:\Test -Filter *.txt -Recurse |
Select-String -Pattern "trial" –SimpleMatch

One line of PowerShell gives you a very powerful way of filtering the files recursively and testing their contents for a given string


January 5, 2013  6:20 AM

Number of processors in a box

Richard Siddaway Richard Siddaway Profile: Richard Siddaway

WMI enables you find the number of processors in your system:

PS> Get-WmiObject -Class Win32_ComputerSystem | fl Number*

NumberOfLogicalProcessors : 2
NumberOfProcessors        : 1

This works fine for Windows Vista/Windows 2008 and above.

Earlier versions of Windows mis-report the number of processors – it counts the number of logical processors reports it as the number of physical processors.

Win32_Processor has the same problem on Windows 2003 and below.

There is a hotfix available from http://support.microsoft.com/kb/932370 that will correct the behaviour of these two WMI classes so that they report correctly


January 4, 2013  11:59 AM

Finding the domain controller that authenticated you

Richard Siddaway Richard Siddaway Profile: Richard Siddaway

A question on my blog asked how do you know which domain controller you are running against when you search Active Directory. Unless you explicitly instruct your script to use a specific domain controller it will use the one to which you authenticated.

You can find the DC to which you authenticated with this simple function

function get-logonserver{
$env:LOGONSERVER -replace "\\", ""
}


January 3, 2013  1:11 PM

Displaying data from multiple servers as HTML

Richard Siddaway Richard Siddaway Profile: Richard Siddaway

A forum question regarding retrieving WMI based data from multiple servers and displaying it as HTML was interesting.  I would approach it like this

$servers = Get-Content -Path C:\scripts\servers.txt            
$data = @()            
foreach ($server in $servers){            
 $compdata = New-Object -TypeName PSObject -Property @{            
  Computer = $server            
  Contactable = $false            
  LastBootTime = ""            
  AllowTSConnections = $false            
 }            
            
 if (Test-Connection -ComputerName $server -Quiet -Count 1){            
   $compdata.Contactable = $true            
               
   $os = Get-WmiObject -Class Win32_OperatingSystem -ComputerName $server            
   $compdata.LastBootTime = $os.ConvertToDateTime($os.LastBootUpTime)            
            
   $ts = Get-WmiObject -Namespace root\cimv2\terminalservices -Class Win32_TerminalServiceSetting -ComputerName $server -Authentication PacketPrivacy            
   if ($ts.AllowTSConnections -eq 1){            
    $compdata.AllowTSConnections = $true            
   }            
 }            
             
 $data += $compdata            
}            
$data            
$data | ConvertTo-Html | Out-File -FilePath c:\scripts\report.html            
Invoke-Item -Path c:\scripts\report.html

Put the list of servers in a text file & read it in via get-content.

use foreach to iterate over the list of servers.

For each server create an object and then test if you can ping the server. Note that the default setting for Contactable is $false so don’t need to deal with that case.

Get the WMI data and set the properties on the object.

Add the object to an array

After you’ve hit all the servers use ConvertTo-Html and write to a file with out-file.

use Invoke-Item to view the report


January 3, 2013  12:48 PM

Ensuring that parameter values are passed to your function

Richard Siddaway Richard Siddaway Profile: Richard Siddaway

A question on the forum about a function had me thinking. The user had defined two parameters for the function and then used Read-Host to get the values.

NO

Much better way is to use an advanced function and make the parameters mandatory

function Getuserdetails {            
[CmdletBinding()]            
param (            
[parameter(Mandatory=$true)]            
[string]$Givenname,             
            
[parameter(Mandatory=$true)]            
[string]$Surname            
)             
            
Get-ADUser -properties telephonenumber,office -Filter {(GivenName -eq $Givenname) -and (Surname -eq $Surname)}             
            
}

If you call the function and don’t give values for the parameters you will be prompted for them

The other point is the –Filter property on get-aduser.  Don’t put quotes round the variable


January 3, 2013  6:01 AM

PowerShell workflow articles

Richard Siddaway Richard Siddaway Profile: Richard Siddaway

I’ve written a series of articles on PowerShell workflows that are appearing on the Scripting Guy blog. The first two in the series have been published at:

http://blogs.technet.com/b/heyscriptingguy/archive/2012/12/26/powershell-workflows-the-basics.aspx

http://blogs.technet.com/b/heyscriptingguy/archive/2013/01/02/powershell-workflows-restrictions.aspx

 

Enjoy


Forgot Password

No problem! Submit your e-mail address below. We'll send you an e-mail containing your password.

Your password has been sent to: