Count number of spaces, and split at the last one - powershell

I have a string simliar to:
c:/folder name/somewhere/application.exe instanceName
(n.b. the space in "folder name" is intentional) I need a way to split this into:
[0]c:/folder name/somewhere/application.exe
[1]instanceName
I was going to use split-path, but apparently there is a bug in powershell v2 that stops me doing this:
Split-Path : Cannot find drive. A drive with the name '
So, I figured if I count how many spaces there are, and then simply use -split() to split it at the last space.
But, I can't see how to count the number of spaces.
I've found lots of examples that talk about using regex to count complex strings, but I just want to count spaces!

Tonnes of ways to do this I imagine but to use your split idea you could do the following.
$split = "c:/folder name/somewhere/application.exe instanceName".Split(" ")
$path = $split[0..($split.count -2)] -Join " "
$instance = $split[-1]
Split the sting by spaces. The number of spaces is represented by the count of strings in the array $split. We join all the strings in the array accept the last intp $path then we take the last entry and assign it to $instance
You could also use .substring and .lastindexof
$string = "c:/folder name/somewhere/application.exe instanceName"
$index = $string.LastIndexOf(" ")
$string.Substring(0,$index)
$string.Substring($index + 1)
I can't see a way to split this directly into an array at this time but outputing as an array would not be a big deal.
$path, $instance
or
$array = #($path,$instance)

You can use a regular expression for this:
$s = "c:/folder name/somewhere/application.exe instanceName"
$s -match '(.*) (.*)$'
$matches[1]
$matches[2]
The special variable $matches is populated if the -match operation is true.
$matches[0] contains the original string, and other indexes will exist for the number of groups (patterns in parenthesis) in the regex. In this case: (.*) (.*)$ we have two groups.

Related

Powershell: combine contents of strings with dots inbetween, ignore empty ones

Our naming convention consists of the first name, insertion, and lastname, all separated by dots. An example:
Stack Overflow = Stack.Overflow
Stack over Flow = Stack.over.flow
These outputs will be used later on in the script for the creation of a mailbox, user account, etc.
I've successfully combined the values of all strings by simply plus-ing them together, like this:
$Convention = $Firstname+"."+$Insertion+"."+$LastName
The values for these strings come from information being put in when the stript runs (Read-Host "....")
Now, I'm struggling with making this more dynamic. Of course, not every person has an insertion in their name. Using the given example, the current output of $Convention would be "Stack..Overflow", instead of "Stack.Overflow".
My question to you is: how can I filter out both, the $Insertion and the extra dot, when $Insertion is empty? It's most likely something very simple, but I can't seem to figure out what it is.
Thanks in advance for any given help!
Kr,
Robbert
I would do
$Convention = ('{0}.{1}.{2}' -f $Firstname, $Insertion, $LastName) -replace '\.+', '.'
The -replace uses regex in the first parameter, so '\.+', '.' means to replace 1 or more consecutive dots by a single dot.
Alternatively you could use regex \.{2,} which reads two or more consecutive dots
Example:
$Firstname = 'Robbert'
$Insertion = ''
$LastName = 'Verwaart'
$Convention = ('{0}.{1}.{2}' -f $Firstname, $Insertion, $LastName) -replace '\.+', '.'
Output:
Robbert.Verwaart
The code below will go through each of your $convention array, if this is an array, and test if the insertion is empty. If the $Insertion variable is empty, the $i will remove the $Insertion variable and the extra .. You need to add this into the script as a test, before creating the mailboxes.
foreach ($i in $convention){
if($insertion -eq "" -or $insertion -eq $null) {
$i= $Firstname+"."+$LastName
} else {
continue
}
}

Wanted to get middle text from PSObject in Powershell

I have a PSObject which contains the following Values
AZREUS/MYVM-0.mydomain.com
AZREUS/MYVM-1.mydomain.com
I need only the VM name stored in a new PS-Object, how can I do that.
The list should return like below.
MYVM-0
MYVM-1
a simple way can be to use -replace operator:
$list = #('AZREUS/MYVM-0.mydomain.com','AZREUS/MYVM-1.mydomain.com')
$list -replace 'AZREUS/'-replace '\.mydomain\.com'
YannCha's answer is an efficient answer if your strings always begin with AZREUS/ and end with .mydomain.com. You can use a single -replace to get the desired result.
$obj = 'AZREUS/MYVM-0.mydomain.com','AZREUS/MYVM-1.mydomain.com'
$obj -replace '^AZREUS/(.*)\.mydomain\.com$','$1'
$1 represents capture group 1, which was created by the first parentheses grouping (). It contains .* contents. See Regex for regex explanation.
Taking the same approach further dynamically, you can use pattern matching. This removes all beginning characters including the first /. Then removes the first . and all characters after it.
$obj -replace '^.*/(.*?)\..*$','$1'
See Regex for regex explanation.
Note that if your object items are not strings, they will need to support being converted to strings or you will have to do that yourself before applying -replace.
Or using -match. Do them one at a time.
'AZREUS/MYVM-0.mydomain.com' -match 'AZREUS/(.*).mydomain.com' > $null; $matches[1]
MYVM-0
'AZREUS/MYVM-1.mydomain.com' -match 'AZREUS/(.*).mydomain.com' > $null; $matches[1]
MYVM-1

Powershell replace last two occurrences of a '/' in file path with '.'

I have a filepath, and I'm trying to remove the last two occurrences of the / character into . and also completely remove the '{}' via Powershell to then turn that into a variable.
So, turn this:
xxx-xxx-xx\xxxxxxx\x\{xxxx-xxxxx-xxxx}\xxxxx\xxxxx
Into this:
xxx-xxx-xx\xxxxxxx\x\xxxx-xxxxx-xxxx.xxxxx.xxxxx
I've tried to get this working with the replace cmdlet, but this seems to focus more on replacing all occurrences or the first/last occurrence, which isn't my issue. Any guidance would be appreciated!
Edit:
So, I have an excel file and i'm creating a powershell script that uses a for each loop over every row, which amounts to thousands of entries. For each of those entries, I want to create a secondary variable that will take the full path, and save that path minus the last two slashes. Here's the portion of the script that i'm working on:
Foreach($script in $roboSource)
{
$logFileName = "$($script.a).txt".Replace('(?<=^[^\]+-[^\]+)-','.')
}
$script.a will output thousands of entries in this format:
xxx-xxx-xx\xxxxxxx\x{xxxx-xxxxx-xxxx}\xxxxx\xxxxx
Which is expected.
I want $logFileName to output this:
xxx-xxx-xx\xxxxxxx\x\xxxx-xxxxx-xxxx.xxxxx.xxxxx
I'm just starting to understand regex, and I believe the capture group between the parenthesis should be catching at least one of the '\', but testing attempts show no changes after adding the replace+regex.
Please let me know if I can provide more info.
Thanks!
You can do this in two fairly simply -replace operations:
Remove { and }
Replace the last two \:
$str = 'xxx-xxx-xx\xxxxxxx\x\{xxxx-xxxxx-xxxx}\xxxxx\xxxxx'
$str -replace '[{}]' -replace '\\([^\\]*)\\([^\\]*)$','.$1.$2'
The second pattern matches:
\\ # 1 literal '\'
( # open first capture group
[^\\]* # 0 or more non-'\' characters
) # close first capture group
\\ # 1 literal '\'
( # open second capture group
[^\\]* # 0 or more non-'\' characters
) # close second capture group
$ # end of string
Which we replace with the first and second capture group values, but with . before, instead of \: .$1.$2
If you're using PowerShell Core version 6.1 or newer, you can also take advantage of right-to-left -split:
($str -replace '[{}]' -split '\\',-3) -join '.'
-split '\\',-3 has the same effect as -split '\\',3, but splitting from the right rather than the left.
A 2-step approach is simplest in this case:
# Input string.
$str = 'xxx-xxx-xx\xxxxxxx\x\{xxxx-xxxxx-xxxx}\xxxxx\xxxxx'
# Get everything before the "{"
$prefix = $str -replace '\{.+'
# Get everything starting with the "{", remove "{ and "}",
# and replace "\" with "."
$suffix = $str.Substring($prefix.Length) -replace '[{}]' -replace '\\', '.'
# Output the combined result (or assign to $logFileName)
$prefix + $suffix
If you wanted to do it with a single -replace operation (with nesting), things get more complicated:
Note: This solution requires PowerShell Core (v6.1+)
$str -replace '(.+)\{(.+)\}(.+)',
{ $_.Groups[1].Value + $_.Groups[2].Value + ($_.Groups[3].Value -replace '\\', '.') }
Also see the elegant PS-Core-only -split based solution with a negative index (to split only a fixed number of tokens off the end) in Mathias R. Jessen's helpful answer.
try this
$str='xxx-xxx-xx\xxxxxxx\x\{xxxx-xxxxx-xxxx}\xxxxx\xxxxx'
#remove bracket and split for get array
$Array=$str -replace '[{}]' -split '\\'
#take all element except 2 last elements, and concat after last elems
"{0}.{1}.{2}" -f ($Array[0..($Array.Length -3)] -join '\'), $Array[-2], $Array[-1]

String coming with an extra new line

Below is the code where I am taking server names from a text file and concatenating with comma.
But when I am printing the value, it is coming with an extra new line after the values.
I tried doing $erversToReboot.Trim(), but didn't helped.
$ServerList = Get-Content "D:\ServerName.txt"
$Servers=""
foreach($Server in $ServerList)
{
$Servers += $Server + ","
}
[string]$ServersToReboot= $Servers.TrimEnd(",")
The output coming as
server1,server2
---one extra line here---
Please let me know what is going wrong here.
Best as I can tell, you're attempting to comma separate your servers. I'd skip the Foreach construct myself and simply use the join operator.
$ServerList = Get-Content -Path 'D:\ServerName.txt'
$ServerList -join ','
This can be done in a single statement, as well.
$ServerList = (Get-Content -Path 'D:\ServerName.txt') -join ','
Tommy
As others have noted, it's in general much simpler to use the -join operator to join the input lines with a specifiable separator.
As for the problem of an extra empty line: Gert Jan Kraaijeveld plausibly suggests that your input file has an extra empty line at the end, while noting that it is actually not what would happen with the code you've posted, which should work fine (despite its inefficiency).
Perhaps the extra line is an artifact of how you're printing the resulting value.
To answer the related question of how to ignore empty lines in the input file:
Assuming that it is OK to simply remove all empty lines from the input, the simplest PowerShell-idiomatic solution is:
#(Get-Content D:\ServerName.txt) -ne '' -join ','
#(Get-Content D:\ServerName.txt) returns the input lines as an array[1] of strings, from which -ne '' then removes empty lines, and the result of which -join joins with separator ,
[1] Get-Content D:\ServerName.txt would return a scalar (single string), if the input file happened to contain only 1 line, because PowerShell generally reports a single output object as itself rather than as a single-element array when pipeline output is collected.
Because of that, #(...), the array-subexpression operator - instead of just (...) - is needed in the above command: it ensures that the output from Get-Command is treated as an array, because the -ne operator acts differently with a scalar LHS and returns a Boolean rather than filtering the LHS's elements: compare 'foo' -ne '' to #('foo') -ne ''.
By contrast, the #(...) is not necessary if you pass the result (directly) to -join (which simply is a no-op with a scalar LHS):
(Get-Content D:\ServerName.txt) -join ','

How can I replace every comma with a space in a text file before a pattern using PowerShell

I have a text file with lines in this format:
FirstName,LastName,SSN,$x.xx,$x.xx,$x.xx
FirstName,MiddleInitial,LastName,SSN,$x.xx,$x.xx,$x.xx
The lines could be in either format. For example:
Joe,Smith,123-45-6789,$150.00,$150.00,$0.00
Jane,F,Doe,987-65-4321,$250.00,$500.00,$0.00
I want to basically turn everything before the SSN into a single field for the name thus:
Joe Smith,123-45-6789,$150.00,$150.00,$0.00
Jane F Doe,987-65-4321,$250.00,$500.00,$0.00
How can I do this using PowerShell? I think I need to use ForEach-Object and at some point replace "," with " ", but I don't know how to specify the pattern. I also don't know how to use a ForEach-Object with a $_.Where so that I can specify the "SkipUntil" mode.
Thanks very much!
Mathias is correct; you want to use the -replace operator, which uses regular expressions. I think this will do what you want:
$string -replace ',(?=.*,\d{3}-\d{2}-\d{4})',' '
The regular expression uses a lookahead (?=) to look for any commas that are followed by any number of any character (. is any character, * is any number of them including 0) that are then followed by a comma immediately followed by a SSN (\d{3}-\d{2}-\d{4}). The concept of "zero-width assertions", such as this lookahead, simply means that it is used to determine the match, but it not actually returned as part of the match.
That's how we're able to match only the commas in the names themselves, and then replace them with a space.
I know it's answered, and neatly so, but I tried to come up with an alternative to using a regex - count the number of commas in a line, then replace either the first one, or the first two, commas in the line.
But strings can't count how many times a character appears in them without using the regex engine(*), and replacements can't be done a specific number of times without using the regex engine(**), so it's not very neat:
$comma = [regex]","
Get-Content data.csv | ForEach {
$numOfCommasToReplace = $comma.Matches($_).Count - 4
$comma.Replace($_, ' ', $numOfCommasToReplace)
} | Out-File data2.csv
Avoiding the regex engine entirely, just for fun, gets me things like this:
Get-Content .\data.csv | ForEach {
$1,$2,$3,$4,$5,$6,$7 = $_ -split ','
if ($7) {"$1 $2 $3,$4,$5,$6,$7"} else {"$1 $2,$3,$4,$5,$6"}
} | Out-File data2.csv
(*) ($line -as [char[]] -eq ',').Count
(**) while ( #counting ) { # split/mangle/join }