PowerShell is awesome if you haven’t used it but it’s absolutely foreign if you’re used to Unix-like Bash-like shells. For instance, there’s no grep, and ls -al won’t work (but ls will and it’ll display all files with details!).

If you switch from Linux to Windows, you might be tempted to either install Cygwin or all of your GNU utilities. Or you might go the Linux subsystem route (which is a totally fair way to go) but if you want to go the PowerShell route, here’s a neat little tip!

What’s grep?

Grep is basically a utility that lets you filter some data using various patterns. There’s a LOT more to it but my own common usage of it is:

$ cat .bash_history | grep ssh
ssh root@
ssh deploy@
ssh ubuntu@

This will filter out the output of cat and return only commands that include ssh. I use this to remind myself of IPs of servers I’ve recently used. Or I might do this:

$ cat package.json | grep webpack
    "start:dev": "webpack --config webpack/dev.config.js",
    "webpack": "3.8.1",
    "webpack-dev-server": "^2.9.4",
    "webpack-merge": "^4.1.1",

to get all of my webpack plugins, scripts, and figure out the latest version of webpack. It’s a handy utility, and I use it all the time. To filter lists of files in a folder and pipe them to another command or whatever else.

Grep is POWERFUL and it’s super featureful but I won’t get into that in this article.

So what does PowerShell have that’s the equivalent?

Introducing Select-String

One thing that’s immediately apparent about PowerShell is that its commands have better naming conventions. In fact, PS script is more like a programming language than Bash.

PowerShell piping is like Bash piping though it has some differences that I won’t go into right now.

cat package.json | Select-String -Pattern webpack
ls ./src/components/ | Select-String -Pattern View

Select-String supports regular expressions so you can also write more complicated stuff like:

C:\> cat post.md | Select-String -Pattern "^\w*:"

title: How to "grep" in PowerShell
published: false

This will extract the front-matter from this post.

How about searching through multiple files?

Absolutely! Grep has a pretty easy syntax for it: grep $PATTERN glob/pattern/**. An -r flag will recursively search through a path.

You might use it to identify where you’re using @select in your Angular application. @select is a decorator provided by the angular-redux store.

$ grep -r "@select" *.ts
comments-list.component.ts:  @select(['comments']) comments: Observable<IComment[]>;
comments-list.component.ts:  @select(authorsSelector) authors: Observable<any>;
comments-list.component.ts:  @select(['topComment']) topComment: Observable<IComment>;

So what about PowerShell? In PowerShell, you have to specify an argument but the explicitness actually makes a lot of sense:

C:\> Select-String -Path *.ts -Pattern "@select"

comments-list.component.ts:51:  @select(['comments']) comments: Observable<IComment[]>;
comments-list.component.ts:53:  @select(authorsSelector) authors: Observable<any>;
comments-list.component.ts:55:  @select(['topComment']) topComment: Observable<IComment>;

Cool, what about recursion? Err…well, to have true recursion working and scan “n” levels deep, you’ll need to string a few commands together so we’ll skip that for now.

Happy PowerShelling!

Advanced Examples

Before I leave, I’d like to impart some fun advanced commands. Like I said, PowerShell piping isn’t like Linux piping. One of the big differences is that PowerShell utilizes “objects”.

So here’s a great example:

C:\> Select-String -Path *.ts -Pattern "@select" | Select Filename, LineNumber, Line, Path | Format-Table

What this will do is: find files and matches to our pattern in all of TypeScript files in our current directory, select only the information we want to see, and transform it into a nice looking table like so (edited to fit site width):

Filename                    LineNumber   Line               Path
--------                    ----------   ----               ----
comments-list.component.ts  51           @select(['comme... C:\comments-list...
comments-list.component.ts  53           @select(authors... C:\comments-list...
comments-list.component.ts  55           @select(['topCo... C:\comments-list...

You can then save it into a file via Out-File <filename> like so:

C:\> Select-String -Path *.ts -Pattern "@select" | Select Filename, LineNumber, Line, Path | Format-Table | Out-File selectors.txt

Or output to a CSV:

C:\> Select-String -Path *.ts -Pattern "@select" | Select Filename, LineNumber, Line, Path | Format-Table | Export-Csv -path selectors.csv -NoTypeInformation

What I love about PowerShell is that this stuff is built-in and it has sensible names. When I read bash scripts, it can be confusing. I mean, what does awk do? What about sed? Can you infer their jobs based on their names? Or what their flags mean?