PowerShell is pretty powerful, but of course a lot of what you do is put other commands and programs together to build a solution. Writing scripts that can find what they need in the environment you run them on is quite handy.
First, let's get oriented. Whenever something needs to resolve a relative path, typically it'll be done from the current path. This is %CD%
in cmd
, and in PowerShell you can get this through Get-Command
.
PS > Get-Location
Path
----
C:\Users\theuser
Note that this returns a PathInfo
object which has with a Path
property. If you want to do something with that path, for example append a string, you'll need to get that property.
Write-Host ((Get-Location).Path + "\foo")
C:\Users\theuser\foo
If you care for it, the alias for Get-Location
is pwd
, the Unix command to print the working directory.
While this is handy, often what you need to know is where the script that you're running is on the file system, so you can find other scripts next to it or perhaps look for other files you've placed related to it.
What you want to use here is the $PSScriptRoot
variable. Note that this won't be set if you're running interactively, so try it out by writing Write-Host $PSScriptRoot
into a .ps1 file, then run it.
In my case, I named the file C:\Users\theuser\AppData\Local\Temp\ps.scratch\foo.ps1.
PS > .\foo.ps1
C:\Users\theuser\AppData\Local\Temp\ps.scratch
Let's say you want to find all markdown files under a directory.
PS > Get-ChildItem somedir\*.md -Recurse
You can start off the current directory by avoiding the somedir\
bit, and you can look in a single directory by removing -Recurse
.
The alias for Get-ChildItem
is, perhaps unsurprisingly, dir
.
Now, just listing everything isn't very useful. Typically you'll want to do something with these. You can pipe the output, or you can assign the results to a variable. This will yield an array of System.IO.FileSystemInfo
objects.
The most common properties you'll access are Name
(without any directory information) and FullName
(including the directories).
You can also access the Directory.Name
property and the Directory.FullName
property, which give you the respective information but for the directory the file is in.
There are a few ways of doing path manipulation in powershell. Let's cover the ones you're most likely to use.
If you have a proper object like a FileSystemInfo object, then you have a number of properties like Name
, FullName
and Extension
. Those can come quite handy.
PowerShell has a Join-Path command, but it's quite a bit trickier to use than you probably care to. While in principle it has the benefit of working on things other than file systems and a few extra features, if you are working with files, it's probably not what you want for simple operations.
My recommendation is to use the Path commands, for example Combine to concatenate paths, and GetDirectoryName with GetFileName to get the parts of a filename.
PS > [IO.Path]::Combine('a', 'b', 'c')
a\b\c
Batch files have where, and PowerShell has Get-Command. This will look at available PowerShell commands, but also scan the PATH directories for executable.
PS > (get-command find) | format-list
Name : find.exe
CommandType : Application
Definition : C:\windows\system32\find.exe
Extension : .exe
Path : C:\windows\system32\find.exe
...
The Path
property is the one you will care the most about.
Now, the script gets quite angry if it doesn't find what you're looking for. Here's a way of making that happier.
$c = (get-command blargh -ErrorAction SilentlyContinue).Path
if ($c) {
write-host hello $c
} else {
write-host "oh no"
}
OK, let's break this down.
get-command
with "blargh". This will demonstrate the failure case. Switch to "find" to see it succeed.If you have installed git on Windows, it turns out there are a lot of utilities that are installed alongside that are very handy.
This little function can be used to find diff
for example, putting together a few of the commands we've used above.
$ErrorActionPreference = "Stop"
function Find-Diff() {
$diff = (get-command diff -ErrorAction SilentlyContinue).Path
if (-Not $diff) {
$git = (get-command git -ErrorAction SilentlyContinue).Path
if (-Not $git) {
throw "Unable to find diff or git on the path"
}
$gitcmddir = [IO.Path]::GetDirectoryName($git)
$gitusrbindir = [IO.Path]::Combine($gitcmddir, "..", "usr", "bin")
$diff = [IO.Path]::Combine($gitusrbindir, "diff.exe")
If (-Not (Test-Path $diff)) {
throw "Unable to find diff, expected it at $diff"
}
}
return $diff
}
$diff = Find-Diff
& $diff --version
If you save and run this script, you should get the diff
banner printed out, or possibly an error message if it cannot be found on your system.
One last word: for a list of common I/O tasks, make sure you check the Common I/O Tasks page.
Happy search!
Tags: coding powershell