Split string in Powershell - powershell

I have sth written in a ".ini" file that i want to read from PS. The file gives the value "notepad.exe" and i want to give the value "notepad" into a variable. So i do the following:
$CLREXE = Get-Content -Path "T:\keeran\Test-kill\test.ini" | Select-String -Pattern 'CLREXE'
#split the value from "CLREXE ="
$CLREXE = $CLREXE -split "="
#everything fine untill here
$CLREXE = $CLREXE[1]
#i am trying to omit ".exe" here. But it doesn't work
$d = $CLREXE -split "." | Select-String -NotMatch 'exe'
How can i do this ?

#Mathias R. Jessen is already answered your question.
But instead of splitting on filename you could use the GetFileNameWithoutExtension method from .NET Path class.
$CLREXE = "notepad.exe"
$fileNameWithoutExtension = [System.IO.Path]::GetFileNameWithoutExtension($CLREXE)
Write-Host $fileNameWithoutExtension # this will print just 'notepad'

-split is a regex operator, and . is a special metacharacter in regex - so you need to escape it:
$CLREXE -split '\.'
A better way would be to use the -replace operator to remove the last . and everything after it:
$CLREXE -replace '\.[^\.]+$'
The regex pattern matches one literal dot (\.), then 1 or more non-dots ([^\.]+) followed by the end of the string $.
If you're not comfortable with regular expressions, you can also use .NET's native string methods for this:
$CLREXE.Remove($CLREXE.LastIndexOf('.'))
Here, we use String.LastIndexOf to locate the index (the position in the string) of the last occurrence of ., then removing anything from there on out

Related

Failing to get Substring from Path in PowerShell

There are these two given paths
$x = 'C:\temp\folder1\data'
$t = 'C:\temp\'
Desired is to get '\folder1\data' to a new variable
I tried
Select-String -Path $x -Pattern $t
which ended in
Select-String: The string C:\temp\ is not a valid regular expression: Invalid pattern 'C:\temp\' at offset 8. Illegal \\ at end of pattern.
Also I tried to replace the backslash with a pipe symbol which ended in
Select-String: Cannot find path 'C:\|temp|folder1|data' because it does not exist.
This
$x -replace $t,""
does bring back
||folder1|data
Which is close.
How would I correctly extract a search string from a path string ins PS ?
You need to escape the string before you can use it as a regex pattern:
PS ~> $x -replace [regex]::Escape($t)
folder1\data
Trim $t before escaping if you don't want to remove the trailing slash:
PS ~> $x -replace [regex]::Escape($t.TrimEnd('\'))
\folder1\data
To ensure the replacement only occurs at the start of the string, add a caret ^, which in regex is a metacharacter designating the "start of string":
$x -replace "^$([regex]::Escape($t.TrimEnd('\')))"

Replace a non-unique line of text under a unique line of text in a text file using powershell

I have the following txt file.
[AppRemover]
Enable=0
[CleanWipe]
Enable=0
[RerunSetup]
Enable=0
How do I change the Enable=0 to Enable=1 under [CleanWipe] only?
Below is how I plan on using the code with my file.
$Path = C:\temp\file.txt
$File = Get-Content -Path $Path
# Code to update file
$File | Out-File $Path
You can use -replace to update the value if it is 0.
$Path = C:\temp\file.txt
(Get-Content $Path -Raw) -replace "(?<text>\[CleanWipe\]\r?\nEnable=)0",'${text}1' |
Set-Content $Path
Using a module that parses INI files will be the best solution though. I'd recommend trying PsIni.
Explanation:
The -Raw switch reads the file contents as a single string. This makes it easier to work with newline characters.
-replace performs a regex match and then replace. Below is the regex match breakdown.
(?<text>) is a named capture group. Anything matched within that capture group can be recalled in the replace string as '${text}'.
\[CleanWipe\] is a literal match of [CleanWipe] while escaping the [] characters with \.
\r? is optional carriage return
\n is the newline character
Enable= is a literal match
0 is a literal match
The replace string is the capture group contents and 1 when a match exists. Technically, a capture group is not needed if you want to use a positive lookbehind instead. The positive lookbehind assertion is (?<=). That solution would look like the following:
$Path = C:\temp\file.txt
(Get-Content $Path -Raw) -replace "(?<=\[CleanWipe\]\r?\nEnable=)0",'1' |
Set-Content $Path
The problem with the -replace solutions as they written is they will update the file regardless of a change actually being made to the contents. You would need to add an extra comparison to prevent that. Other issues could be extra white space on any of these lines. You can account for that by adding \s* where you think those possibilities may exist.
Alternative With More Steps:
$file = Get-Content $Path
$TargetIndex = $file.IndexOf('[CleanWipe]') + 1
if ($file[$TargetIndex] -match 'Enable=0') {
$file[$TargetIndex] = 'Enable=1'
$file | Set-Content $Path
}
This solution will only update the file if it meets the match condition. It uses the array method IndexOf() to determine where [CleanWipe] is. Then assumes the line you want to change is in the next index.
IndexOf() is not the only way to find an index. The method requires that your line match the string exactly. You can use Select-String (case-insensitive by default) to return a line number. Since it will be a line number and not an index (indexes start at 0 while line numbers start at 1), it will invariably be the index number you want.
$file = Get-Content $Path
$TargetIndex = ($file | Select-String -Pattern '[CleanWipe]' -SimpleMatch).LineNumber
if ($file[$TargetIndex] -match 'Enable=0') {
$file[$TargetIndex] = 'Enable=1'
$file | Set-Content $Path
}

Change pipe delimited file to comma delimited in Powershell

I have a pipe delimited .TXT file. I need to change the delimiter to a comma instead but still keep the file extension as .TXT. The file looks like this:
Column 1 |Column 2
13|2019-09-30
96|2019-09-26
173|2019-09-25
I am using Windows Powershell 5.1 version for my script.
I am using the following code:
$file = New-Object System.IO.StreamReader -Arg "c:\file.txt"
$outstream = [System.IO.StreamWriter] "c:\out.txt"
while ($line = $file.ReadLine()) {
$s = $line -replace '|', ','
$outstream.WriteLine($s)
}
$file.close()
$outstream.close()
Instead of just replacing the pipe with a comma, the output file looks like this:
C,o,l,u,m,n, 1 , |,C,o,l,u,m,n, 2
1,3,|,2,0,1,9,-,0,9,-,3,0
9,6,|2,0,1,9,-,0,9,-,2,6
1,7,3,|,2,0,1,9,-,0,9,-,2,5
The only problem with your answer is in how you try to replace the | characters in the input:
$s = $line -replace '|', ',' # WRONG
PowerShell's -replace operator expects a regex (regular expression) as its first RHS operand, and | is a regex metacharacter (has special meaning)[1]; to use it as a literal character, you must \-escape it:
# '\'-escape regex metacharacter '|' to treat it literally.
$s = $line -replace '\|', ','
While PowerShell's -replace operator is very flexible, in simple cases such as this one you can alternatively use the [string] type's .Replace() method, which performs literal string replacements and therefore doesn't require escaping (it's also faster than -replace):
# Use literal string replacement.
# Note: .Replace() is case-*sensitive*, unlike -replace
$s = $line.Replace('|', ',')
[1] | denotes an alternation in a regex, meaning that the subexpressions on either side are matched against the input string and one of them matching is sufficient; if your full regex is just |, it effectively matches the empty string before and after each character in the input, which explains your symptom; e.g., 'foo' -replace '|', '#' yields #f#o#o#
You can use Import-Csv and Export-Csv by specifying the -Delimiter.
Import-Csv -Delimiter '|' -Path "c:\file.txt" | Export-Csv -Delimiter ',' -Path "c:\file.txt" -NoTypeInformation
You will find the -split and -join operators to be of interest.
Get-Content -Path "C:\File.TXT" | ForEach-Object { ($_ -split "\|") -join "," } | Set-Content -Path "C:\Out.TXT"

Read text after last backslash

I am would like to read in the text after the last backslash from my text file. Currently I have:
$data=Get-Content "C:\temp\users.txt"
The users.txt file contains path from users home directories
\\myserver.home.com\users\user1.test
How can I pick out the users account (user1.test) name at the end of the line of text so I can use it as a variable?
You can use a simple regex to remove everything until and including the last slash:
$user = $data -replace '.*\\'
Since you are dealing with file paths, you can use GetFileName:
$data=Get-Content "C:\temp\users.txt"
$name=[System.IO.Path]::GetFileName($data)
$HomeDirArray = Get-Content "C:\temp\users.txt" | Split-Path -Leaf will give you an array that can be iterated through using ForEach (e.g., ForEach ($User in $HomeDirArray) {...}.
You can use Split and [-1] to get the string after the last backslash:
$data = Get-Content "C:\temp\users.txt"
$file = ($data -split '\\')[-1]
This uses two backslashes as backslash is a regex special character (escape) so the first slash is escaping the second.

does not work split on powershell

I have code, which split string to array. So can you help me, why this is doesn't work?
$var="test.1->test.2"
$arr=$var.Split("->")
$arr[0]#show correct: "test.1"
$arr[1]#doesn't show...
The string Split() method takes an array of characters and splits the string on any character that is in the array. Powershell also has a -split operator that takes a delimiter. I think you probably wanted to use the -split operator here.
Here's the difference. The first splits at both the '-' and the '>', the second splits on the '->':
PS C:\> $var.Split("->")
test.1
test.2
PS C:\> $var -split "->"
test.1
test.2
Note that split takes a regular expression pattern rather than a simple string. While that is fine for this particular pattern other punctuation could provide problems requiring you to escape special characters in the pattern or use the SimpleMatch option:
PS C:\> "a[b" -split "[",0,"SimpleMatch"
a
b
It works. But it did split the string by "-" OR ">", so $arr[1] has empty string between "-" and ">", and "test.2" is in $arr[2].
So you can either:
$var="test.1->test.2"
$arr=$var.Split("->")
write-host $arr[0]
write-host $arr[2]
or:
$var="test.1->test.2"
$arr=$var.Split("->") | select -First 1 -Last 1
write-host $arr[0]
write-host $arr[1]
or something like:
$var="test.1->test.2"
$arr= $($var -replace "->","#").Split("#")
write-host $arr[0]
write-host $arr[1]