Copy file with square brackets [ ] in the filename and use * wildcard - powershell

I'm using PowerShell on Windows 7, and writing a script to copy a bunch of files from one folder structure to another. Kind of like compiling. The PowerShell Copy-Item cmdlet thinks that square brackets, [ ], are wildcards of some kind, and I am not able to escape them for some reason.
I can't use -LiteralPath, because I want to use an asterisk * wildcard since the filename has a date as part of the filename, and the date changes. The date is used as a version number.
This post was helpful, but no amount of ticks (2x or 4x per bracket) escapes the square brackets.
I am not receiving an error; PowerShell behaves the same as if I entered in the wrong filename.
This is the specific line I'm working on:
#to Fusion Server
Copy-item -Path $FSG\$SW\0.RoomView.Notes\starter\"[RoomView] Versions explained*.pdf" -Destination $FSG\$containerFolder\$rootFolder\"Fusion Server"\
And this is the whole thing:
# Compiles the Fusion packet for distribution
###############################
###########Variables###########
###############################
#folder structure
$FSG = "F:\FSG"
$containerFolder = "Packet.Fusion for IT and AV Professionals"
$rootFolder = "Fusion for IT and AV pros $(Get-Date -format “MM-dd-yyyy”)"
$subRoot1 = "Fusion Server"
$subRoot2 = "Scheduling Enhancement and Panels"
$subRoot2sub1 = "Scheduling Panels"
$subRoot3 = "SQL Server"
#source folders
$HW = "0.Hardware"
$3SMDoc = "0.Hardware\TPMC-3SM.Documentation"
$4SMDoc = "0.Hardware\TPMC-4SM.Documentation"
$4SMDDoc = "0.Hardware\TPMC-4SM-FD.Documentation"
$730Doc = "0.Hardware\TSW-730.Documentation"
$730OLH = "0.Hardware\TSW-730.OLH"
$CENRVS = "0.Hardware\CEN-RVS.Notes"
$ProjMgmt = "0.Project Management"
$SW = "0.Software"
$RVLicensing = "0.Software\0.RoomView.License"
$RVNotes = "0.Software\0.RoomView.Notes"
$SQLLicensing = "0.Software\database.SQL.Licensing"
$SQLNotes = "0.Software\database.SQL.Notes"
$FRVMarketing = "0.Software\Fusion RV.Marketing"
$FRVNetworking = "0.Software\Fusion RV.Networking"
$FRVNotes = "0.Software\Fusion RV.Notes"
###############################
#create the directory structure
###############################
md -Path $FSG\$containerFolder -Name $rootFolder
cd $FSG\$containerFolder\$rootFolder
md "eControl and xPanels"
md "Fusion Server" #$subRoot1
md "Getting Started as a User"
md "Project Management"
md "RoomView Connected Displays"
md "Scheduling Enhancement and Panels" #$subRoot2
md "SQL Server" #$subRoot3
cd $FSG\$containerFolder\$rootFolder\$subRoot1
md "CEN-RVS"
md "Licenseing Information"
md "Networking"
md "Official Documentation"
md "Prerequisites, including powerShell script"
md "Product Info"
md "Requirements"
md "Tech Info"
md "Windows Authentication to Fusion RV"
cd $FSG\$containerFolder\$rootFolder\$subRoot2
md "Outlook Add-in"
md "Scheduling Panels" #$subRoot2sub1
cd $FSG\$containerFolder\$rootFolder\$subRoot2\$subRoot2sub1
md "TPMC-3SM"
md "TPMC-4SM"
md "TPMC-4SM-FD"
md "TSW-730"
cd $FSG\$containerFolder\$rootFolder\$subRoot3
md "Multi-database model only"
md "SQL Licensing"
cd $FSG\$containerFolder
#reset current folder
###############################
#copy the files
###############################
#Copy-Item -Path C:\fso\20110314.log -Destination c:\fsox\mylog.log
#To the root
Copy-item -Path $FSG\$ProjMgmt\starter\"Fusion Support Group Contact info*.pdf" -Destination $FSG\$containerFolder\$rootFolder\
Copy-item -Path $FSG\$containerFolder\"Fusion for IT and AV professionals release notes.txt" -Destination $FSG\$containerFolder\$rootFolder\
#to eControl and xPanels
Copy-item -Path $FSG\$SW\xpanel.Notes\starter\*.* -Destination $FSG\$containerFolder\$rootFolder\"eControl and xPanels"\
#to Fusion Server
Copy-item -Path $FSG\$SW\0.RoomView.Notes\starter\"[RoomView] Versions explained*.pdf" -Destination $FSG\$containerFolder\$rootFolder\"Fusion Server"\
What can I do to escape the square brackets and still use a wildcard filename part of the Copy-Item cmdlet?

In this situation, you have to use double-backticks with single quotes in order to escape the brackets. You can also use quadruple backticks when you use double quoted strings.
So the fixed line of code is:
Copy-item -Path $FSG\$SW\0.RoomView.Notes\starter\'``[RoomView``] Versions explained*.pdf' -Destination $FSG\$containerFolder\$rootFolder\'Fusion Server'\
Another good resource on file paths and wired characters etc. is to read this article: Taking Things (Like File Paths) Literally
EDIT
Thanks to #mklement0 for highlighting that the true cause of this
inconsistency is because of a bug currently in
PowerShell1.
This bug causes escaping of wildcard characters, as well as backticks
with the default -Path parameter to behave differently than other
parameters e.g. the -Include and -Filter parameters.
To expand on #mklement0's excellent answer, and comments, and other answers below:
To better understand why we need single quotes and two back ticks in this situation; (and to highlight the bug and inconsistencies) let's run through some examples to demonstrate what is going on:
Get-Item, and associated cmdlets (Get-ChildItem, Copy-Item, etc.), handle the -Path parameter differently when dealing with a combination of escaped wildcard characters and unescaped wildcard characters *at the same time***!
TLDR: The underlying reason that we need a combination of single quotes and double backticks is how the underlying PowerShell provider parses the -Path parameter string for wildcards. It appears to parse it once for the escape characters, and a second time for the evaluation of the wildcard.
Let's go through some examples to demonstrate this odd outcome:
First, let's create two files to test with called File[1]a.txt and File[1]b.txt
"MyFile" | Set-Content '.\File`[1`]a.txt'
"MyFriend" | Set-Content '.\File`[1`]b.txt'
We'll try different ways to get the file. We know that Square brackets [ ] are wildcards, and so we need to escaped them with the backtick character.
We will try to get one file explicitly.
Let's start by using single quoted literal strings:
PS C:\> Get-Item 'File[1]a.txt'
PS C:\> Get-Item 'File`[1`]a.txt'
Directory: C:\
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a---- 2019-09-06 5:42 PM 8 File[1]a.txt
PS C:\> Get-Item 'File``[1``]a.txt'
Directory: C:\
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a---- 2019-09-06 5:42 PM 8 File[1]a.txt
For single quoted strings, one backtick is all that is required to retrieve the file, but two backticks also work.
Using Double quoted strings we get:
PS C:\> Get-Item "File[1]a.txt"
PS C:\> Get-Item "File`[1`]a.txt"
PS C:\> Get-Item "File``[1``]a.txt"
Directory: C:\
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a---- 2019-09-06 5:42 PM 8 File[1]a.txt
For double quoted strings, as expected, we can see that we need two backticks to make it work.
Now, we want to retrieve both files and use a wildcard.
Let's start with single quotes:
PS C:\> Get-Item 'File[1]*.txt'
PS C:\> Get-Item 'File`[1`]*.txt'
PS C:\> Get-Item 'File``[1``]*.txt'
Directory: C:\
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a---- 2019-09-06 5:42 PM 8 File[1]a.txt
-a---- 2019-09-06 5:49 PM 10 File[1]b.txt
With the single quotes, when we have a wildcard character, we need two sets of backticks. One to escape the bracket, and a second backtick to escape the backtick that we used to escape the bracket when the wildcard is evaluated.
Similarly for double quotes:
PS C:\> Get-Item "File[1]*.txt"
PS C:\> Get-Item "File`[1`]*.txt"
PS C:\> Get-Item "File``[1``]*.txt"
PS C:\> Get-Item "File```[1```]*.txt"
PS C:\> Get-Item "File````[1````]*.txt"
Directory: C:\
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a---- 2019-09-06 5:42 PM 8 File[1]a.txt
-a---- 2019-09-06 5:49 PM 10 File[1]b.txt
With double quotes it's a little more verbose to evaluate with a wildcard. In this case, we need four sets of back ticks. For double quotes we need two backticks to escape the bracket, and another two backticks to escape the escape characters once it comes to evaluation of the star wildcard.
EDIT
As #mklement0 mentions, this behavior with the -Path parameter is inconsistent, and behaves differently than the -Include parameter, where only a single backtick is required to properly escape the brackets. This may be "fixed" in a later version of PowerShell.
1 As of Windows PowerShell v5.1 / PowerShell Core 6.2.0-preview.3

An overview and some background information:
In order to effectively escape a character that you want to be interpreted verbatim as part of a wildcard expression, it must be `-escaped as seen by the target cmdlet (its underlying PowerShell drive provider).
Ensuring that can get tricky, because ` (backtick) is also used as the escape character in double-quoted strings ("...") and unquoted command arguments (which for the most part behave like double-quoted strings).
Note: The scenario in the question doesn't allow use of -LiteralPath, but in cases where you know a path to be a concrete, literal path, use of the -LiteralPath (which can be shorted to -lp in PowerShell Core) is the best choice - see this answer.
When passing an argument to the wildcard-supporting -Path parameter of a PowerShell drive provider-related cmdlet (Get-ChildItem, Copy-Item, Get-Content, ...) and you want [ and ] to be treated verbatim rather than as a character set/range expression:
String-literal representations:
'file`[1`].txt'
` chars. are preserved as-is inside '...', so the target cmdlet sees them, as intended.
"file``[1``].txt"
``, i.e. doubling is needed inside "..." in order to preserve a single ` in the resulting string (the first ` is the (double-quoted) string-internal escape character, and the second ` is the character it escapes, to be passed through).
file``[1``].txt
Ditto for unquoted command arguments, which (for the most part) act like "..."
Caveat: Due to a bug - see this GitHub issue - mixing (unescaped) ? or * with escaped [ and ] requires the latter to be doubly escaped (with ``, as seen by the target cmdlet / provider):
If you wanted to match literal filename file[1].txt with a wildcard pattern that matches [ and ] literally while also containing special character * (to match any run of characters), instead of the expected 'file`[1`]*', you'll have to use 'file``[1``]*' (sic); with a double-quoted or unescaped argument you then have to effectively use quadruple backticks: "file````[1````]*" / file````[1````]* - see this answer for more.
Note that direct use of wildcards with the -like operator is not affected:
'a[b' -like 'a`[*' is - correctly - $true,
whereas 'a[b' -like 'a``[*' - rightfully - complains about an invalid pattern.
Similarly, parameters -Include and -Exclude are not affected.
-Filter plays by different rules to begin with: [...] as a construct isn't supported at all, and [ and ] chars. are always considered literals (again, see this answer).
To escape a path string programmatically, via a variable, use:
$literalName = 'file[1].txt'
$escapedName = [WildcardPattern]::Escape($literalName) # -> 'file`[1`].txt'

I use this:
Copy-Item $file.fullname.replace("[", "``[").replace("]", "``]") $DestDir

The way that Powershell automatically tab-completes the filename is usually the best way,
Example:
copy-item '.\file`[test`].txt'

On PowerShell v 2.0 and up the escape character to use is the backslash. For example, if we want to remove the brackets from this string "[Servername: QA01]" which is the sort of output we get from the Exchange Admin PowerShell cmdlet activity in System Center Orchestrator, we use the following logic:
$string -replace '\[','' -replace '\]',''
>Servername: QA01
This is pretty weird. See, you have to use a single-quote (which normally implies in PowerShell 'evaluate this precisely as written', so this is very odd syntax).
Don't feel bad for not figuring this out on your own, this is very odd syntax.

Apparently, square brackets need double-backticks to escape, which is unusual. Reference here.
You're sure that doesn't work? I've seen it referred to a few times.
Edit: Yes, it works, you used double quotes instead of backticks.
Double quote is above the apostrophe character, next to the Enter key. Backtick is right underneath the Escape key, sharing the key with the tilde, ~.

One option is to get the filenames using the legacy dir, which will let you use the * wildcard character, but doesn't try to "blob" the square brackets. Then feed that list to move-item using -literalpath
cmd /c dir *]* /b |
foreach { Move-Item -LiteralPath $_ -Destination <destination path> }

Assuming nothing else matches, you can use ? instead of the brackets. A file named "a[h-j]", copying to directory "foo":
copy-item a?h-j? foo

There's a difference between ' and `:
The first is the single quote that is the non-shift character on the " key.
The second is the backtick that I thought I was using but actually wasn't. It's the nonshift character on the ~ key.
This works:
# to Fusion Server
Copy-item -Path $FSG\$SW\0.RoomView.Notes\starter\'``[RoomView``] Versions explained*.pdf' -Destination $FSG\$containerFolder\$rootFolder\"Fusion Server"\

Related

Get-ChildItem -Filter parameter not working as expected on Windows PowerShell 5.1 [duplicate]

I have a folder that contains both .xls, .xlsx and .xlsm files, and would like to filter just the .xls files.
Why is the following line not working as I'd expect it to? I see .xls, .xlsx and .xlsm results.
Get-ChildItem $(Get-Location) -Filter *.xls | ForEach-Object { $_.Extension }
The -Filter parameter's wildcard matching is not performed by PowerShell, it is passed through to the filesystem provider and ultimately the Windows API.
The matching performed there is burdened with many legacy behaviors and quirks, including the one you saw:
In Windows PowerShell, -Filter *.xls effectively behaves like -Filter *.xls*. Therefore, -Filter *.xls matches both foo.xls and foo.xlsx, for instance; this happens, because the 8.3 (short) file names are also being matched behind the scenes; for instance, foo.xlsx's 8.3 file name looks something like FOO~1.XLS; note the truncation (and capitalization) of .xlsx to .XLS.
While the short-name matching behavior no longer occurs in PowerShell [Core] v6+, fortunately, other legacy quirks persist[1], as does the most notable difference (which won't go away): only PowerShell wildcard expressions (see about_Wildcards) support character ranges / sets via [...] (e.g., [a-z]) - they're not supported with -Filter.
Use of the -Filter parameter is in general still preferable to -Path / -Include (see below) due to its superior performance (filtering happens at the source, instead of after the fact in PowerShell).
The workaround is to use the -Path parameter in order to use PowerShell's wildcard matching:
Get-ChildItem -Path (Join-Path (Get-Location) *.xls) | ForEach-Object { $_.Extension }
# Or, more simply
Get-ChildItem -Path $PWD/*.xls | ForEach-Object Extension
Note: With -Recurse you'd use the -Include parameter instead; without -Recurse, the behavior of -Include (and -Exclude) is unintuitive, unfortunately - see the bottom section of this answer.
[1] Notable other quirks:
Multiple consecutive ? wildcards can match names with fewer characters.
E.g., Get-ChildItem -Filter ??.txt matches aa.txt and unexpectedly also a.txt
Pattern *. matches extension-less file and directory names.
E.g., Get-ChildItem -File -Filter *. finds all files (-File) whose names do not have an extension (e.g., file); this quirk can actually be useful, in that it is the simplest and best-performing way to locate extension-less files (-Path *. does not work, because it looks for a file name literally ending in a .).
Note: This was temporarily changed in PowerShell Core 6.x (as of 6.2.3), but the behavior is back as of PowerShell Core 7.0.
Conversely, *.* includes extension-less file and directory names as well.
See this excellent answer by Zenexer for the backstory and the gory details.
-filter matches the short version of the filename. This has been covered elsewhere. It's resolved in powershell 6.
echo hi > file.xlsx
dir -filter *.xls
Directory: C:\users\js\foo
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a---- 2/11/2020 9:54 AM 4 file.xlsx
cmd /c dir /x
Volume in drive C is DISK
Volume Serial Number is A11A-AA11
Directory of C:\users\js\foo
02/11/2020 09:54 AM <DIR> .
02/11/2020 09:54 AM <DIR> ..
02/11/2020 09:54 AM 4 FILE~1.XLS file.xlsx
1 File(s) 4 bytes
2 Dir(s) 3,380,170,752 bytes free

Is there a way to filter file names by ~?

I am attempting to find all file names in a root directory that contain a ~ so they can be changed.
I have tried using the escape character (-filter "~" instead of -filter "~"), however when doing that it grabs nothing.
I have also tried putting the ~ in a string variable, but that also did not work, it just pulled the entire root folder again
I know there are about 200 files that have ~ in their name in the test directory, so this should grab 200 files.
[string] $rootfolder = "C:\Documents\Powershell-Test",
[string] $folder = "",
[string] $subfolder = "",
#Place Illegal Character Here
[string] $illegalCharacter = "~",
#place replacement Phrase here
[string] $replacementPhrase = "",
$files = Get-ChildItem -Path $rootfolder -Recurse -Filter *$illegalCharacter*
I use the same script I used to find other unwanted characters. When I ran this script for # and &, it grabbed all the files with the character, remo and printed them to a csv file.
When I ran the ~, it grabbed every file in the root folder.
I did not include the code to print the file because that code is not altered when changing what is being searched for.
-Filter qualifies the path parameter. This means you need wildcards if you don't know the exact string name of target directory or file. If you don't know where the ~ exists in the file names, you need to use *~* when qualifying the path. Per Matthias R. Jessen, -Filter "*~*" will not produce the desired result because *~* matches every single file with 8 or more characters in the name because PowerShell offloads filtering to Windows, and Windows applies the filter to both the real name and the 8dot3 name of the file system items.
The following will produce the desired result. Note this was tested on PowerShell v5.1.
$rootfolder = "C:\Documents\Powershell-Test"
$illegalCharacter = '~'
$files = Get-ChildItem -Path $rootfolder -Recurse |
Where-Object {$_.Name -like "*$IllegalCharacter*"}
Putting the wildcards in the path works for me. The -filter *~* matching short filenames thing doesn't happen in PS 6, so it appears to be a bug.
get-childitem -path $rootfolder\*$illegalCharacter* -recurse
It even matches the number in the short filename.
cmd /c dir /x
07/17/2019 02:19 PM 10 VERYLO~2.TXT verylongname.txt
ls . *2*
-a---- 7/17/2019 2:19 PM 10 verylongname.txt
"get-childitem -filter" seems like something to be avoided, like ">".

Batch copy and rename files with PowerShell

I'm trying to use PowerShell to batch copy and rename files.
The original files are named AAA001A.jpg, AAB002A.jpg, AAB003A.jpg, etc.
I'd like to copy the files with new names, by stripping the first four characters from the filenames, and the character before the period, so that the copied files are named 01.jpg, 02.jpg, 03.jpg, etc.
I have experience with Bash scripts on Linux, but I'm stumped on how to do this with PowerShell. After a couple of hours of trial-and-error, this is as close as I've gotten:
Get-ChildItem AAB002A.jpg | foreach { copy-item $_ "$_.name.replace ("AAB","")" }
(it doesn't work)
Note:
* While perhaps slightly more complex than abelenky's answer, it (a) is more robust in that it ensures that only *.jpg files that fit the desired pattern are processed, (b) shows some advanced regex techniques, (c) provides background information and explains the problem with the OP's approach.
* This answer uses PSv3+ syntax.
Get-ChildItem *.jpg |
Where-Object Name -match '^.{4}(.+).\.(.+)$' |
Copy-Item -Destination { $Matches.1 + '.' + $Matches.2 } -WhatIf
To keep the command short, the destination directory is not explicitly controlled, so the copies will be placed in the current dir. To ensure placement in the same dir. as the input files, use
Join-Path $_.PSParentPath ($Matches.1 + '.' + $Matches.2) inside { ... }.
-WhatIf previews what files would be copied to; remove it to perform actual copying.
Get-ChildItem *.jpg outputs all *.jpg files - whether or not they fit the pattern of files to be renamed.
Where-Object Name -match '^.{4}(.*).\.(.+)$' then narrows the matches down to those that fit the pattern, using a regex (regular expression):
^...$ anchors the regular expression to ensure that it matches the whole input (^ matches the start of the input, and $ its end).
.{4} matches the first 4 characters (.), whatever they may be.
(.+) matches any nonempty sequence of characters and, due to being enclosed in (...), captures that sequence in a capture group, which is reflected in the automatic $Matches variable, accessible as $Matches.1 (due to being the first capture group).
. matches the character just before the filename extension.
\. matches a literal ., due to being escaped with \ - i.e., the start of the extension.
(.+) is the 2nd capture group that captures the filename extension (without the preceding . literal), accessible as $Matches.2.
Copy-Item -Destination { $Matches.1 + '.' + $Matches.2 } then renames each input file based on the capture-group values extracted from the input filenames.
Generally, directly piping to a cmdlet, if feasible, is always preferable to piping to the Foreach-Object cmdlet (whose built-in alias is foreach), for performance reasons.
In the Copy-Item command above, the target path is specified via a script-block argument, which is evaluated for each input path with $_ bound to the input file at hand.
Note: The above assumes that the copies should be placed in the current directory, because the script block outputs a mere filename, not a path.
To control the target path explicitly, use Join-Path inside the -Destination script block.
For instance, to ensure that the copies are always placed in the same folder as the input files - irrespective of what the current dir. is - use:
Join-Path $_.PSParentPath ($Matches.1 + '.' + $Matches.2)
As for what you've tried:
Inside "..." (double-quoted strings), you must use $(...), the subexpression operator, in order to embed expressions that should be replaced with their value.
Irrespective of that, .replace ("AAB", "") (a) breaks syntactically due to the space char. before ( (did you confuse the [string] type's .Replace() method with PowerShell's -replace operator?), (b) hard-codes the prefix to remove, (c) is limited to 3 characters, and (d) doesn't remove the character before the period.
The destination-location caveat applies as well: If your expression worked, it would only evaluate to a filename, which would place the resulting file in the current directory rather than the same directory as the input file (though that wouldn't be a problem, if you ran the command from the current dir. or if that is your actual intent).
In Powershell:
(without nasty regexs. We hates the regexs! We does!)
Get-ChildItem *.jpg | Copy-Item -Destination {($_.BaseName.Substring(4) -replace ".$")+$_.Extension} -WhatIf
Details on the expression:
$_.BaseName.Substring(4) :: Chop the first 4 letters of the filename.
-replace ".$" :: Chop the last letter.
+$_.Extension :: Append the Extension
Not Powershell, but Batch File:
(since someone wants to be ultra-pedantic about comments)
#echo off
setlocal enabledelayedexpansion
for %%a in (*.jpg) do (
::Save the Extension
set EXT=%%~xa
::Get the Source Filename (no extension)
set SRC_FILE=%%~na
::Chop the first 4 chars
set DST_FILE=!SRC_FILE:~4!
::Chop the last 1 char.
set DST_FILE=!DST_FILE:~,-1!
:: Copy the file
copy !SRC_FILE!!EXT! !DST_FILE!!EXT! )
try this:
Get-ChildItem "C:\temp\Test" -file -filter "*.jpg" | where BaseName -match '.{4,}' |
%{ Copy-Item $_.FullName (Join-Path $_.Directory ("{0}{1}" -f $_.BaseName.Substring(4, $_.BaseName.Length - 5), $_.Extension)) }

Prevent PowerShell Select-String from parsing colon as a drive letter

I have a profile that gets executed every time I open my PowerShell window. Since I do some SSH'ing, I need to ensure that my gets added right after I start up PowerShell. But I want to avoid asking my passphrase for the SSH as long as it's already added to the ssh-agent. The solution to this problem is described here, but for the Linux shell.
I have converted the solution described there to a PowerShell equivalent, which is as follows:
$ssh_add = "$env:ProgramFiles/Git/usr/bin/ssh-add.exe"
$ssh_keygen = "$env:ProgramFiles/Git/usr/bin/ssh-keygen.exe"
$my_key_path = "$env:USERPROFILE/.ssh/id_rsa"
$my_ssh_key = & $ssh_keygen -lf $my_key_path
$ssh_keys = & $ssh_add -l
if (!(Select-String -Pattern $my_ssh_key -Path $ssh_keys -SimpleMatch -Quiet))
{
& $ssh_add -t 5h $my_key_path
}
The SSH keys happen to contain colons, which PowerShell seems to think are drive letters. This results in the following error message:
Select-String : Cannot find drive. A drive with the name '4096 SHA256' does not exist.
The SSH keys in the following form:
$my_ssh_key = 4096 SHA256:somelongSSHkey some.email#stackoverflow.com (RSA)
$ssh_keys = 4096 SHA256:anotherlongSSHkey /c/Users/MyUser/.ssh/id_rsa (RSA)
How can I prevent the colon from being parsed as a drive letter seperator?
-Path is supposed to be a path. The value you are providing to it will, naturally, be interpreted as a path. You could escape the colon, but that doesn't solve your problem of telling Select-String to treat something as a path when it isn't a path.
You want the -InputObject (can be shortened, as all PS arguments can) parameter where you're using -Path right now. That will match the literal string, rather than treating it as a file to search. You can also pipe the contents of $ssh_keys to Select-String instead, and not specify any path.
if (!(Select-String -Input $ssh_keys -Pattern $my_ssh_key -SimpleMatch -Quiet))
...
See https://technet.microsoft.com/library/hh849903.aspx for full documentation.

Using PowerShell to add an extension to files

I have a directory of files that I'd like to append file extension to as long as they don't have an existing, specified extension. So add .txt to all file names that don't end in .xyz. PowerShell seems like a good candidate for this, but I don't know anything about it. How would I go about it?
Here is the Powershell way:
gci -ex "*.xyz" | ?{!$_.PsIsContainer} | ren -new {$_.name + ".txt"}
Or to make it a little more verbose and easier to understand:
Get-ChildItem -exclude "*.xyz"
| WHere-Object{!$_.PsIsContainer}
| Rename-Item -newname {$_.name + ".txt"}
EDIT: There is of course nothing wrong with the DOS way either. :)
EDIT2: Powershell does support implicit (and explicit for that matter) line continuation and as Matt Hamilton's post shows it does make thing's easier to read.
+1 to EBGreen, except that (at least on XP) the "-exclude" parameter to get-childitem doesn't seem to work. The help text (gci -?) actually says "this parameter does not work properly in this cmdlet"!
So you can filter manually like this:
gci
| ?{ !$_.PSIsContainer -and !$_.Name.EndsWith(".xyz") }
| %{ ren -new ($_.Name + ".txt") }
Consider the DOS command FOR in a standard shell.
C:\Documents and Settings\Kenny>help for
Runs a specified command for each file in a set of files.
FOR %variable IN (set) DO command [command-parameters]
%variable Specifies a single letter replaceable parameter.
(set) Specifies a set of one or more files. Wildcards may be used.
command Specifies the command to carry out for each file.
command-parameters
Specifies parameters or switches for the specified command.
...
In addition, substitution of FOR variable references has been enhanced.
You can now use the following optional syntax:
%~I - expands %I removing any surrounding quotes (")
%~fI - expands %I to a fully qualified path name
%~dI - expands %I to a drive letter only
%~pI - expands %I to a path only
%~nI - expands %I to a file name only
%~xI - expands %I to a file extension only
%~sI - expanded path contains short names only
%~aI - expands %I to file attributes of file
%~tI - expands %I to date/time of file
%~zI - expands %I to size of file
%~$PATH:I - searches the directories listed in the PATH
environment variable and expands %I to the
fully qualified name of the first one found.
If the environment variable name is not
defined or the file is not found by the
search, then this modifier expands to the
empty string
Found this helpful while using PowerShell v4.
Get-ChildItem -Path "C:\temp" -Filter "*.config" -File |
Rename-Item -NewName { $PSItem.Name + ".disabled" }