Pro Windows PowerShell

Hristo Deshev

Mentioned 2

Here is your best companion to the capabilities and power that PowerShell offers. Inside this book, you’ll discover the object-oriented features of the shell and how they help in extracting and manipulating data. You’ll then learn how to use those features to solve real-world problems: manipulating files, working with text, monitoring systems, and performing operations over the network. In addition, you’ll find freely available tools and extensions that help you get results with Windows PowerShell fast.

More on Amazon.com

Mentioned in questions and answers.

I am really used to doing grep -iIr on the Unix shell but I haven't been able to get a PowerShell equivalent yet.

Basically, the above command searches the target folders recursively and ignores binary files because of the "-I" option. This option is also equivalent to the --binary-files=without-match option, which says "treat binary files as not matching the search string"

So far I have been using Get-ChildItems -r | Select-String as my PowerShell grep replacement with the occasional Where-Object added. But I haven't figured out a way to ignore all binary files like the grep -I command does.

How can binary files be filtered or ignored with Powershell?

So for a given path, I only want Select-String to search text files.

EDIT: A few more hours on Google produced this question How to identify the contents of a file is ASCII or Binary. The question says "ASCII" but I believe the writer meant "Text Encoded", like myself.

EDIT: It seems that an isBinary() needs to be written to solve this issue. Probably a C# commandline utility to make it more useful.

EDIT: It seems that what grep is doing is checking for ASCII NUL Byte or UTF-8 Overlong. If those exists, it considers the file binary. This is a single memchr() call.

Ok, after a few more hours of research I believe I've found my solution. I won't mark this as the answer though.

Pro Windows Powershell had a very similar example. I had completely forgot that I had this excellent reference. Please buy it if you are interested in Powershell. It went into detail on Get-Content and Unicode BOMs.

This Answer to a similar questions was also very helpful with the Unicode identification.

Here is the script. Please let me know if you know of any issues it may have.

# The file to be tested
param ($currFile)

# encoding variable
$encoding = ""

# Get the first 1024 bytes from the file
$byteArray = Get-Content -Path $currFile -Encoding Byte -TotalCount 1024

if( ("{0:X}{1:X}{2:X}" -f $byteArray) -eq "EFBBBF" )
{
    # Test for UTF-8 BOM
    $encoding = "UTF-8"
}
elseif( ("{0:X}{1:X}" -f $byteArray) -eq "FFFE" )
{
    # Test for the UTF-16
    $encoding = "UTF-16"
}
elseif( ("{0:X}{1:X}" -f $byteArray) -eq "FEFF" )
{
    # Test for the UTF-16 Big Endian
    $encoding = "UTF-16 BE"
}
elseif( ("{0:X}{1:X}{2:X}{3:X}" -f $byteArray) -eq "FFFE0000" )
{
    # Test for the UTF-32
    $encoding = "UTF-32"
}
elseif( ("{0:X}{1:X}{2:X}{3:X}" -f $byteArray) -eq "0000FEFF" )
{
    # Test for the UTF-32 Big Endian
    $encoding = "UTF-32 BE"
}

if($encoding)
{
    # File is text encoded
    return $false
}

# So now we're done with Text encodings that commonly have '0's
# in their byte steams.  ASCII may have the NUL or '0' code in
# their streams but that's rare apparently.

# Both GNU Grep and Diff use variations of this heuristic

if( $byteArray -contains 0 )
{
    # Test for binary
    return $true
}

# This should be ASCII encoded 
$encoding = "ASCII"

return $false

Save this script as isBinary.ps1

This script got every text or binary file I tried correct.

I need to watch when certain processes are started or stopped on a Windows machine. I'm currently tapped into the WMI system and querying it every 5 seconds, but this causes a CPU spike every 5 seconds because WMI is WMI. Is there a better way of doing this? I could just make a list of running processes and attach an Exited event to them through the System.Diagnostics Namespace, but there is no Event Handler for creation.

I've had CPU spikes when listening to WMI events in cases where I have failed to detach properly from my events on exit/cleanup. You might want to check you are not "leaking" WMI event subscriptions. Just in case detach from the event as early as possible and make sure you always do it.

To illustrate further, here's an example from my PowerShell book that listens to WMI events using the PSEventing library:

Add-PSSnapin PSEventing -ErrorAction SilentlyContinue

$queryString = @' SELECT * FROM __InstanceModificationEvent WITHIN 10 WHERE TargetInstance ISA 'Win32_Service' AND TargetInstance.Name = 'w3svc' AND TargetInstance.State = 'Stopped' '@

$query = New-Object System.Management.WQLEventQuery ` -argumentList $queryString

$watcher = New-Object System.Management.ManagementEventWatcher($query)

Connect-EventListener watcher EventArrived

$watcher.Start()

echo "Waiting for the W3CSVC service to stop..." Get-Event -wait | foreach { Write-Host -foreground Red "The W3SVC service has stopped!" }

$watcher.Stop()

Disconnect-EventListener watcher EventArrived

echo "done"

If I do not do the Disconnect-EventListener bit upon script exit, I get CPU spikes the third or fourth time I attach to the event. My guess is that the system still tries to deliver events.