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@0.0.0.0
ssh deploy@0.0.0.0
ssh ubuntu@0.0.0.0
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
description:
tags:
This will extract the front-matter from this post (at the time of writing this section).
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?