Replacing $_ substring value in powershell - powershell

I am trying to make further use of a wonderful piece of code I found when I tried to replace text at a specified line.
However trying to get it to read $_.Substring() and then using $_ -replace is giving me troubles; although I get no error messages the text does not get replaced.
Here is code that does not work:
$content = Get-Content Z:\project\folder\subfolder\newindex2.html
$content |
ForEach-Object {
if ($_.ReadCount -ge 169 -and $_.ReadCount -le 171) {
$a = $_.Substring(40,57)
$linked = '' + $a + ''
$_ -replace $a,$linked
} else {
$_
}
} |
Set-Content Z:\project\folder\subfolder\newindex2.html
The whole point is to make the content of a cell in a html table column link to a file on a webserver with the same name as what's in the cell.
I didn't have any luck trying my hand at regex trying to match the filenames, but since I managed to make it so the text that's to be replaced always ends up at the same position, I figured I'd try positional replacement instead.
The text that is to be replaced is always 57 characters long and always starts at position 40.
I looked at the variables getting set, and everything gets set correctly, except that
$_ -replace $a,$linked
does not replace anything.
Instead, the whole file just gets written anew with nothing changed. Can anyone please point to what I am missing and/or point to how to reach the result more easily? Maybe I'm using Substring wrong and should be using something else?

The first item in the right-hand argument of -replace is a regex pattern, so depending on what the substring contains, some of the characters might be regex control characters.
You can either escape it:
$_ -replace $([regex]::Escape($a)),$linked
Or use the String.Replace() method, which does not use regex:
$_.Replace($a,$linked)
Finally, as #Matt points out, you might want to avoid the find-and-replace approach altogether, since you already know at which character indices you need to insert your new value:
$_.Remove(40,57).Insert(40,$linked)

Related

I want to remove certain parts of a certain string in Powershell 7.2.4, how do I do it?

I have the string "url": "https://maven.fabricmc.net/net/fabricmc/fabric-installer/0.11.0/fabric-installer-0.11.0.jar". I want to remove everything from the string except https://maven.fabricmc.net/net/fabricmc/fabric-installer/0.11.0/fabric-installer-0.11.0.jar (I want to remove the quotes as well).
How do I go about it?
$str = """url"": ""https://maven.fabricmc.net/net/fabricmc/fabric-installer/0.11.0/fabric-installer-0.11.0.jar"""
$str.Split(""": """)[1].Replace("""","")
The shortest I could think of
Might have the incorrect indexnumber, just test switching '3'
( $str -split """ )[3]
You could use the -replace operator and extract the section you want.
'"url": "https://maven.fabricmc.net/net/fabricmc/fabric-installer/0.11.0/fabric-installer-0.11.0.jar"' -replace '"url": "(.+)"','$1'
However since this appears to be in JSON format you may be better off using ConvertFrom-Json on the entire thing and then accessing the property through dot notation.
It's slightly easier than trying to regex or string match sections of JSON.
$converted = Get-Content file.json | ConvertFrom-Json
$converted.url

Splitting a string and selecting a substring in PowerShell

I am attempting to isolate and return a small variable string from a larger string.
I am struggling because the larger string I am extracting from is in list format. I can split this into substrings successfully, but I do not know how to select one of these substrings without returning the entire string. The string is generated by a command line process.
$StringList
AppTitle1.1.1221.aaa111
AppSubTitle
AnotherAppTitle1.1.1221.aaa111
AnotherAppSubTitle
...and so on
I can split the list string into substrings by line using regular expressions to split at whitespace (there is no whitespace within any given line).
$StringList -split "\s"
Once I have split the string into the desired substrings, however, I am not sure how to select the desired substring. The length of the list (i.e. the number of apps present in it) and the location of the app I need to retrieve the title of within that list are entirely variable, so I cannot simply use substring reference numbers. I've tried several approaches to selecting the substring, but each has simply returned the entire string, or nothing at all.
Here are two approaches I've attempted. The first returns the entire string list and the second returns nothing.
$DesiredAppTitle = Select-String -InputObject $StringList -Pattern "AnotherAppTitle"
or
$DesiredAppTitle = foreach ($_.substring in $StringList)
{
if ($_.substring -contains "AnotherAppTitle")
{
return $_.name
}
}
What I'd like for it to return is:
AnotherAppTitle1.1.1221.aaa111
I'm sure there are a million ways to do this, so if neither of my approaches seems like a good fit, I'm open to other suggestions. Any assistance would be greatly appreciated. Thanks in advance!
# Multi-line input string.
$StringList = #'
AppTitle1.1.1221.aaa111
AppSubTitle
AnotherAppTitle1.1.1221.aaa111
AnotherAppSubTitle
'#
# Split it into whitespace-separated tokens.
$tokens = -split $StringList
# Match the token of interest.
$tokens -match '^AnotherAppTitle'
The above yields:
AnotherAppTitle1.1.1221.aaa111
Note the use of regex-matching operator with anchor ^ to ensure that the search term matches at the start of a token, and the use of the unary form of the -split operator, which splits the input by any nonempty whitespace runs.
As for what you tried:
If you pass a multi-line string to Select-String, it is considered a single "line" and, in case of a match, that whole "line" is output.
foreach ($_.substring in $StringList) won't even run, because $_.substring is not a valid iteration variable (you shouldn't use $_, which is an automatic variable, as an enumeration variable at all, and the .substring access breaks the syntax).
If you used $_ instead of $_.substring, the loop would technically work (even though, again, $_ shouldn't be used as an iteration variable), but the loop would only execute once, for the entire multi-line string.
Even if $_.substring did refer to a line (it doesn't), -contains is the wrong operator to use, because it tests if a LHS collection contains the RHS value in full.
Also, use break to exit a loop, not return.
Using the -match approach as demonstrated at the top is the better approach, but if you did want to solve this with a foreach loop:
$DesiredAppTitle = foreach ($token in -split $StringList) {
if ($token -match '^AnotherAppTitle') { $token; break }
}

troubles with powershell where clause

i am trying to parse a website for specific data.
$strings = $body.split(";")
$strings2 = $strings.Where({$_ -like ("*recordData[`"key`"]*")})
i figured out that the square brackets are to blame, i use
$strings2 = $strings.Where({$_ -like ("*recordData*")})
and it works fine, albeit returning way more results then i need.
is there a way i can just search for "recordData[key]"
$body is just the entirety of a returned webpage
thanks.
EDIT:
as requested the input data is like this
rs.currentColumn = 0;
recordData["dataGridExtraRow"] = 0;
recordData["rownum"] = "0";
recordData["key"] = '3354087';
recordData["factory"] = "cr";
in the end i need the 3354087 number, but just picking out the lines i needed was the issue, after that i can pick apart the string fine.
however, i ended up using the .contains, thanks for the suggestion.
sort of facepalmed after i saw it though.
If you are using the like operator then you are going to be having an issue with the open square bracket which is a wildcard character in PowerShell. See About_Wildcards
...
[] - Matches range of characters
...
A use case would be something like this which would return true.
"recordFata" -like "record[DF]ata"
So if you are going to be using -like you need to escape the brackets using backticks in a single quoted sting. You can avoid that by using other methods that function in the same way you intended.
Other options
String .Contains() Method
"sdfafdrecordData[key]asdfasdfas".Contains("recordData[key]")
Fairly basic and no need to worry about special characters for the most part.
Regex
"sdfafdrecordData[key]asdfasdfas" -match "recordData\[key]"
Note that the square braces are also regex metacharacters that need to be escaped as well.
Try using single quotes and escaping with backticks.
You can also simplify using PowerShell syntax:
$strings = $body -split ';' -like '*recordData`[key`]*'

Pipes in replace causing line to be duplicated

I have a script that I need to replace a couple of lines in. The first replace is going fine but the second is wiping out my file and duplicating the line multiple times.
My code
(get-content $($sr)) -replace 'remoteapplicationname:s:SHAREDAPP',"remoteapplicationcmdline:s:$($sa)" | Out-File $($sr)
(get-content $($sr)) -replace 'remoteapplicationprogram:s:||SHAREDAPP',"remoteapplicationprogram:s:||$($sa)" | Out-File $($sr)
The first replace works perfectly. The second one is causing this:
remoteapplicationprogram:s:||stagaredrremoteapplicationprogram:s:||stagarederemoteapplicationprogram:s:||stagareddremoteapplicationprogram:s:||stagarediremoteapplicationprogram:s:||stagaredrremoteapplicationprogram:s:||stagarederemoteapplicationprogram:s:||stagaredcremoteapplicationprogram:s:||stagaredtremoteapplicationprogram:s:||stagaredcremoteapplicationprogram:s:||stagaredlremoteapplicationprogram:s:||stagarediremoteapplicationprogram:s:||stagaredpremoteapplicationprogram:s:||stagaredbremoteapplicationprogram:s:||stagaredoremoteapplicationprogram:s:||stagaredaremoteapplicationprogram:s:||stagaredrremoteapplicationprogram:s:||stagareddremoteapplicationprogram:s:||stagared:remoteapplicationprogram:s:||stagarediremoteapplicationprogram:s:||stagared:remoteapplicationprogram:s:||stagared1remoteapplicationprogram:s:||stagared
etc...
Is this because of the ||? If so, how do I get around it?
Thanks!
To begin with, you should be using slightly more meaningful names for your variables. Especially if you want someone else to be reviewing your code.
The gist of your issue is that -replace supports regexes (regular expressions), and you have regex control characters in your pattern string. Consider the following simple example, and notice everywhere the replacement string is found:
PS C:\Users\Matt> "ABCD" -replace "||", "bagel"
bagelAbagelBbagelCbagelDbagel
-replace is also an array operator, so it works on every line of the input file, which is nice. For simplicity's sake, if you are not using a regex, you should just consider using the string method .Replace(), but it is case-sensitive, so that might not be ideal. So let's escape those control characters in the easiest way possible:
$patternOne = [regex]::Escape('remoteapplicationname:s:SHAREDAPP')
$patternTwo = [regex]::Escape('remoteapplicationprogram:s:||SHAREDAPP')
(get-content $sr) -replace $patternOne, "remoteapplicationcmdline:s:$sa" | Out-File $($sr)
(get-content $sr) -replace $patternTwo, "remoteapplicationprogram:s:||$sa" | Out-File $($sr)
Now we get both patterns matched as you have them written. Run $patternTwo on the console to see what has changed to it! $patternOne, as written, has no regex control characters in it, but it does not hurt to use the escape method if you are just expecting simple matching.
Aside from the main issue pointed out, there is also some redundancy and misconception that can be addressed here. I presume you are updating a source file to replace all occurrences of those strings, yes? Well, you don't need to read the file in twice, given that you can chain -replace:
$patternOne = [regex]::Escape('remoteapplicationname:s:SHAREDAPP')
$patternTwo = [regex]::Escape('remoteapplicationprogram:s:||SHAREDAPP')
(get-content $sr) -replace $patternOne, "remoteapplicationcmdline:s:$sa" -replace $patternTwo, "remoteapplicationprogram:s:||$sa" |
Set-Content $sr
Perhaps that will do what you intended.
You might notice that I've removed the subexpressions operators ($(...)) around your variables. While they have their place, they don't need to be used here. They are only needed inside more complicated strings, like when you need to expand object properties or something.

Add quotes to each column in a CSV via Powershell

I am trying to create a Powershell script which wraps quotes around each columns of the file on export to CSV. However the Export-CSV applet only places these where they are needed, i.e. where the text has a space or similar within it.
I have tried to use the following to wrap the quotes on each line but it ends up wrapping three quotes on each column.
$r.SURNAME = '"'+$r.SURNAME+'"';
Is anyone able to share how to forces these on each column of the file - so far I can just find info on stripping these out.
Thanks
Perhaps a better approach would be to simply convert to CSV (not export) and then a simple regex expression could add the quotes then pipe it out to file.
Assuming you are exporting the whole object $r:
$r | ConvertTo-Csv -NoTypeInformation `
| % { $_ -replace ',(.*?),',',"$1",' } `
| Select -Skip 1 | Set-Content C:\temp\file.csv
The Select -Skip 1 removes the header. If you want the header just take it out.
To clarify what the regex expression is doing:
Match: ,(.*?),
Explanation: This will match section of each line that has a comma followed by any number of characters (.*) without being greedy (? : basically means it will only match the minimum number of characters that is needed to complete the match) and the finally is ended with a comma. The parenthesis will hold everything between the two commas in a match variable to be used later in the replace.
Replace: ,"$1",
Explanation: The $1 holds the match between the two parenthesis mention above in the match. I am surrounding it with quotes and re-adding the commas since I matched on those as well they must be replaced or they are simply consumed. Please note, that while the match portion of the -replace can have double quotes without an issue, the replace section must be surrounded in single quotes or the $1 gets interpreted by PowerShell as a PowerShell variable and not a match variable.
You can also use the following code:
$r.SURNAME = "`"$($r.SURNAME)`""
I have cheated to get what I want by re-parsing the file through the following - guess that it acts as a simple find and replace on the file.
get-content C:\Data\Downloads\file2.csv
| foreach-object { $_ -replace '"""' ,'"'}
| set-content C:\Data\Downloads\file3.csv
Thanks for the help on this.