In my PowerShell script, I'm running Select-String over a number of files, looking for a string passed into it via a variable ($id):
foreach ($file in (ls "path\to\files")) {
$found = $false
$found = Select-String -Path $file $id -Quiet
if ($found) {
break
}
}
Unfortunately, the $id variable sometimes things like "\C" or "\T", which Select-String tries to interpret as escape sequences. These are not valid escape sequences, so Select-String throws an error. They are not intended to be escape sequences (e.g., it could be part of a file path such as "C:\Test"), so how can I prevent PowerShell/Select-String from interpreting them as such? I've tried using parentheses around $id with no luck.
Use the static escape() method, it instructs the regular expression engine to interpret these characters literally rather than as metacharacters:
$id = [regex]::escape($id)
You can also turn the command to a one liner (-path can take a collection of files):
Select-String -Path path\to\files\\* -Pattern ([regex]::escape($id)) -Quiet
Select-String has a -SimpleMatch parameter that will cause the cmdlet to do simple string matches instead of regular expressions. If you change the script to do:
$found = Select-String -Path $file $id -Quiet -SimpleMatch
it should work as desired.
If the $id string already contains something like TAB when it's passed to you then I'm not aware of a built in method to safely escape it back to "\t". You need to make sure your script is passed the correct string in the first place. I.e. it needs to passed 0x5C74 (\t) not 0x09 (TAB). So the escaping needs to be done when the the search string is first defined.
Regex.Escape will escape TAB -> \t but will also escape any of these characters that have meaning within regular expressions:
\, *, +, ?, |, {, [, (,), ^, $,., #, and white space
e.g. . -> \.
Related
$file = 'c:\temp\config.ini'
# login.ruby.authentication.key=eskimopie
$pattern = [regex] "(.*?login\.ruby\.authentication\.key)=(.*?).*"
$secret = '12345678'
$text = (Get-Content -Path c:\temp\config.ini)
$value = $text -match "$pattern"
$text -replace "$pattern",'$1=$secret' | Set-Content config.new
The problem is it replaces the line with
login.ruby.authentication.key)=$secret (in that, it actually puts $secret instead of 12345678. I am looking for how to get this to put the value of $secret instead of the word $secret.
When used inside single quoted literals, variables do not get expanded. You need to use double quoted string literal that allows string interpolation.
However, the first $ should not be expanded. To tell PS not to interpolate it, add a backtick before it. See about_Quoting_Rules:
To prevent the substitution of a variable value in a double-quoted string, use the backtick character (`)(ASCII 96), which is the Windows PowerShell escape character.
So, replace with "`$1=$secret" where `$1 will pass a literal $1 string to the regex engine, and $secret will get interpolated to 12345678 before passing it to the regex engine.
I have a list of files that contain either of the two strings:
"stuff" or ";stuff"
I'm trying to write a PowerShell Script that will return only the files that contain "stuff". The script below currently returns all the files because obviously "stuff" is a substring of ";stuff"
For the life of me, I cannot figure out how to only matches file that contain "stuff", without a preceding ;
Get-Content "C:\temp\list\list.txt" |
Where-Object { Select-String -Quiet -Pattern "stuff" -SimpleMatch $_ }
Note: C:\temp\list\list.txt contains a list of file paths that are each passed to Select-String.
Thanks for the help.
You cannot perform the desired matching with literal substring searches (-SimpleMatch).
Instead, use a regex with a negative look-behind assertion ((?<!..)) to rule out stuff substrings preceded by a ; char.: (?<!;)stuff
Applied to your command:
Get-Content "C:\temp\list\list.txt" |
Where-Object { Select-String -Quiet -Pattern '(?<!;)stuff' -LiteralPath $_ }
Regex pitfalls:
It is tempting to use [^;]stuff instead, using a negated (^) character set ([...]) (see this answer); however, this will not work as expected if stuff appears at the very start of a line, because a character set - whether negated or not - only matches an actual character, not the start-of-the-line position.
It is then tempting to apply ? to the negated character set (for an optional match - 0 or 1 occurrence): [^;]?stuff. However, that would match a string containing ;stuff again, given that stuff is technically preceded by a "0-repeat occurrence" of the negated character set; thus, ';stuff' -match '[^;]?stuff' yields $true.
Only a look-behind assertion works properly in this case - see regular-expressions.info.
To complement #mklement0's answer, I suggest an alternative approach to make your code easier to read and understand:
#requires -Version 4
#(Get-Content -Path 'C:\Temp\list\list.txt').
ForEach([IO.FileInfo]).
Where({ $PSItem | Select-String -Pattern '(?<!;)stuff' -Quiet })
This will turn your strings into objects (System.IO.FilePath) and utilizes the array functions ForEach and Where for brevity/conciseness. Further, this allows you to pipe the paths as objects which will be accepted by the -Path parameter into Select-String to make it more understandable (I find long lists of parameter sets difficult to read).
The example code posted won't actually run, as it will look at each line as the -Path value.
What you need is to get the content, select the string you're after, then filter the results with Where-Object
Get-Content "C:\temp\list\list.txt" | Select-String -Pattern "stuff" | Where-Object {$_ -notmatch ";stuff"}
You could create a more complex regex if needed, but depends on what your result data from your files looks like
$file = 'c:\temp\config.ini'
# login.ruby.authentication.key=eskimopie
$pattern = [regex] "(.*?login\.ruby\.authentication\.key)=(.*?).*"
$secret = '12345678'
$text = (Get-Content -Path c:\temp\config.ini)
$value = $text -match "$pattern"
$text -replace "$pattern",'$1=$secret' | Set-Content config.new
The problem is it replaces the line with
login.ruby.authentication.key)=$secret (in that, it actually puts $secret instead of 12345678. I am looking for how to get this to put the value of $secret instead of the word $secret.
When used inside single quoted literals, variables do not get expanded. You need to use double quoted string literal that allows string interpolation.
However, the first $ should not be expanded. To tell PS not to interpolate it, add a backtick before it. See about_Quoting_Rules:
To prevent the substitution of a variable value in a double-quoted string, use the backtick character (`)(ASCII 96), which is the Windows PowerShell escape character.
So, replace with "`$1=$secret" where `$1 will pass a literal $1 string to the regex engine, and $secret will get interpolated to 12345678 before passing it to the regex engine.
I have a text file and want to regex/replace something with the content of a variable in PowerShell.
File: my.json
Variable in Powershell $version
Search for: version : "something"
Replace "something" with the content of the variable $version
Here is what I tried. Search and replace works as expected but the result is
version : "$version".
(Get-Content my.json) -replace '(?<pre>"version"[\s]*:[\s]*)(?<V>"[^\"]*")', '$1"$version"' | Out-File my.json
To be able to use variables in the replacement string you need to use a double-quoted replacement string, meaning that you need to escape backreferences and nested double quotes:
(Get-Content my.json) -replace '...', "`$1`"$version`"" | ...
Ansgar's answer is perfectly valid, but ` escape sequences can be ugly and hinder readability.
I would personally use the -f format operator to concatenate the '$1' string literal and the value of $version:
(Get-Content my.json) -replace '...',('$1{0}' -f $version)
I'm trying to replace a word to some php code
$filecontent = [regex]::Replace($filecontent, $myword, $phpcode)
But the $phpcode have some php code using also a Special variable $_
<?php $cur_author = (isset($_GET['author_name'])) ? get_user_by('slug', $author_name) : get_userdata(intval($author)); ?>
The problem is when the code is replace in $filecontent it replaces the $_ variable from the php code ( $_GET ) with it have on the pipeline.
This not happen with the other variables like $author_name .
How can I resolve this?
Does this work for you?
$filecontent = [regex]::Replace($filecontent, $myword, {$phpcode})
In a regex replace operation the $_ is a reserved substituion pattern that represents the entire string
http://msdn.microsoft.com/en-us/library/az24scfc.aspx
Wrapping it in braces makes it a scriptblock delegate, bypassing the normal regex pattern matching algorithms for doing the replacement.
You have two options. First use a single quoted string and PowerShell will treat that as a verbatim string (C# term) i.e. it won't try to string interpolate:
'$_ is passed through without interpretation'
The other option is to escape the $ character in a double quoted string:
"`$_ is passed through without interpretation"
When I'm messing with a regex I will default to using single quoted strings unless I have a variable that needs to be interpolated inside the string.
Another possibility is that $_ is being interpreted by regex as a substitution group in which case you need to use the substitution escape on the $ e.g. $$.
Im not sure I am following you correctly, but does this help?
$file = path to your file
$oldword = the word you want to replace
$newword = the word you want to replace it with
If the Oldword you are replacing has special charactes ( ie. \ or $ ) then you must escape them first. You can escape them by putting a backslash in front of the special character. The Newword, does not need to be escaped. A $ would become "\$".
(get-content $file) | foreach-object {$_ -replace $oldword,$NewWord} | Set-Content $file