How to find a specific number of digits in string - powershell

I don't understand the Regex :(
I want to find if a path contains only 7 digits
For an example:
C:\Users\3D Objects\1403036 --> the result should be 1403036
C:\Users\358712\1403036 --> the result should be 1403036
and so on
I have tried:
$FilesPath -match '([\d{1,7}]{7})')
and
$FilesPath -match '(\d{7})')
Currently I am working with that:
$FilesPath = Read-Host -Prompt
if ($Matches[1].Length -eq '7') {
$FolderNumber = $Matches[1]
}
This is not right because there is no match if the path contains the number 3 in the path
If this is the case:
C:\Users\3D Objects\1403036854 --> More than 7 digits the result should be empty
or
C:\Users\3874113353D Objects\1403036 --> Should return result for 1403036
I don't have an array, just want to get if there is a number with exactly 7 digits and don't if contains less or more than 7 digits

You mean something like this?
# as example the paths as aray to loop over
'C:\Users\3D Objects\1403036', 'C:\Users\358712\1403036',
'C:\Users\somewhere\1234567', 'C:\Users\3D Objects\1403036854' | ForEach-Object {
# return the number anchored at the end of the string with exactly 7 digits
([regex]'\D(\d{7})$').Match($_).Groups[1].Value
}
Output:
1403036
1403036
1234567
This:
$path = 'C:\Users\3D Objects\1403036'
$result = ([regex]'\D(\d{7})(?:\D|$)').Match($path).Groups[1].Value
directly assigns the match to variable $result and will be the matching numeric value if it matches or $null. Regex method .Match() does not populate the $matches array.
Using the regex operator, which does populate the $matches array, you can also do this:
if ($path -match '\D(\d{7})(?:\D|$)') {
$result = $matches[1]
}
Regex details:
\D # Match a single character that is NOT a “digit” (any decimal number in any Unicode script)
( # Match the regex below and capture its match into backreference number 1
\d # Match a single character that is a “digit” (any decimal number in any Unicode script)
{7} # Exactly 7 times
)
(?: # Match the regular expression below
# Match this alternative (attempting the next alternative only if this one fails)
\D # Match a single character that is NOT a “digit” (any decimal number in any Unicode script)
|
# Or match this alternative (the entire group fails if this one fails to match)
$ # Assert position at the end of the string, or before the line break at the end of the string, if any (line feed)
)

Related

question about powershell text manipulation

I apologise for asking the very basic question as I am beginner in Scripting.
i was wondering why i am getting different result from two different source with the same formatting. Below are my sample
file1.txt
Id Name Members
122 RCP_VMWARE-DMZ-NONPROD DMZ_NPROD01_111
DMZ_NPROD01_113
123 RCP_VMWARE-DMZ-PROD DMZ_PROD01_110
DMZ_PROD01_112
124 RCP_VMWARE-DMZ-INT.r87351 DMZ_TEMPL_210.r
DMZ_DECOM_211.r
125 RCP_VMWARE-LAN-NONPROD NPROD02_20
NPROD03_21
NPROD04_22
NPROD06_24
file2.txt
Id Name Members
4 HPUX_PROD HPUX_PROD.3
HPUX_PROD.4
HPUX_PROD.5
i'm trying to display the Name column and with this code i'm able to display the file1.txt correctly.
PS C:\Share> gc file1.txt |Select-Object -skip 1 | foreach-object { $_.split(" ")[1]} | ? {$_.trim() -ne "" }
RCP_VMWARE-DMZ-NONPROD
RCP_VMWARE-DMZ-PROD
RCP_VMWARE-DMZ-INT.r87351
RCP_VMWARE-LAN-NONPROD
However with the file2 im getting a different output.
PS C:\Share> gc .\file2.txt |Select-Object -skip 1 | foreach-object { $_.split(" ")[1]} | ? {$_.trim() -ne "" }
4
changing the code to *$_.split(" ")[2]}* helps to display the output correctly
However, i would like to have just 1 code which can be apply for both situation.appreciate if you can help me to sort this.. thank you in advance...
This happens because the latter file has different format.
When examined carefully, one notices there are two spaces between 4 and HPUX_PROD strings:
Id Name Members
4 HPUX_PROD HPUX_PROD.3
^^^^
On the first file, there is a single space between number and string:
Id Name Members
122 RCP_VMWARE-DMZ-NONPROD DMZ_NPROD01_111
^^^
As how to fix the issue depends if you need to match both file formats, or if the other has simply a typing error.
The existing answers are helpful, but let me try to break it down conceptually:
.Split(" ") splits the input string by each individual space character, whereas what you're looking for is to split by runs of (one or more) spaces, given that your column values can be separated by more than one space.
For instance 'a b'.split(' ') results in 3 array elements - 'a', '', 'b' - because the empty string between the two spaces is considered an element too.
The .NET [string] type's .Split() method is based on verbatim strings or character sets and therefore doesn't allow you to express the concept of "one ore more spaces" as a split criterion, whereas PowerShell's regex-based -split operator does.
Conveniently, -split's unary form (see below) has this logic built in: it splits each input string by any nonempty run of whitespace, while also ignoring leading and trailing whitespace, which in your case obviates the need for a regex altogether.
This answer compares and contrasts the -split operator with string type's .Split() method, and makes the case for routinely using the former.
Therefore, a working solution (for both input files) is:
Get-Content .\file2.txt | Select-Object -Skip 1 |
Foreach-Object { if ($value = (-split $_)[1]) { $value } }
Note:
If the column of interest contains a value (at least one non-whitespace character), so must all preceding columns in order for the approach to work. Also, column values themselves must not have embedded whitespace (which is true for your sample input).
The if conditional both extracts the 2nd column value ((-split $_)[1]) and assigns it to a variable ($value = ), whose value then implicitly serves as a Boolean:
Any nonempty string is implicitly $true, in which case the extracted value is output in the associated block ({ $value }); conversely, an empty string results in no output.
For a general overview of PowerShell's implicit to-Boolean conversions, see this bottom section of this answer.
Since this sort-of looks like csv output with spaces as delimiter (but not quite), I think you could use ConvertFrom-Csv on this:
# read the file as string array, trim each line and filter only the lines that
# when split on 1 or more whitespace characters has more than one field
# then replace the spaces by a comma and treat it as CSV
# return the 'Name' column only
(((Get-Content -Path 'D:\Test\file1.txt').Trim() |
Where-Object { #($_ -split '\s+').Count -gt 1 }) -replace '\s+', ',' |
ConvertFrom-Csv).Name
Shorter, but because you are only after the Name column, this works too:
((Get-Content -Path 'D:\Test\file2.txt').Trim() -replace '\s+', ',' | ConvertFrom-Csv).Name -ne ''
Output for file1
RCP_VMWARE-DMZ-NONPROD
RCP_VMWARE-DMZ-PROD
RCP_VMWARE-DMZ-INT.r87351
RCP_VMWARE-LAN-NONPROD
Output for file2
HPUX_PROD

Function search from the files 20.0.0.1 to 20.0.0.21

I have 21 files from 20.0.0.0001 to 20.0.0.0021 in my directory D:\Updates3 .
I have a function "search" with 3 parameters: the directory, le first number of my file, the last number of my file
My problem is when I enter the 3 parameters in my function search:
the directory: D:\Updates3
the first number: 20
the las number: 1
I have 3 results
20.0.0.0001
20.0.0.0011
20.0.0.0021
whereas, I want only the result 20.0.0.0001
What is the solution please?
here is my code:
$UpdatePath = "D:\Updates3"
function search {
param (
[string]$UpdatePath,
[int]$M,
[int]$j
)
$UpdatePath = "D:\Updates3"
$Updates = (Get-ChildItem -Path $UpdatePath).BaseName
foreach ($item in $Updates) {
if ($item -like "$M.0.0.???$j" ) {
write-host $item
}
}
}
search $Updates 20 1
It is a pitty your function doesn't really return anything and only writes to the console the file(s) found.
Why not turn this into a function that actually returns a FileInfo object you can get all kinds of properties from and if needed use its methods?
Also, try using the Powershell Verb-Noun naming convention for functions.
The below code doesn't cast the the basename of each file into a [version] type, but uses regex to both ensure the file's BaseName is in the format you describe, ignoring other files, but also it captures the first and last numbers to compare against:
function Find-File {
param (
[string]$Path,
[int]$M,
[int]$j
)
Get-ChildItem -Path $Path -File |
Where-Object {$_.BaseName -match '^(\d+)\.\d+\.\d+\.(\d+)$'} |
Where-Object {[int]$matches[1] -eq $M -and [int]$matches[2] -eq $j}
}
# find and (if found) return the file as FileInfo object
$theFile = Find-File "D:\Updates3" 20 1
Regex details
^ Assert position at the beginning of the string
( Match the regular expression below and capture its match into backreference number 1
\d Match a single digit 0..9
+ Between one and unlimited times, as many times as possible, giving back as needed (greedy)
)
\. Match the character “.” lterally
\d Match a single digit 0..9
+ Between one and unlimited times, as many times as possible, giving back as needed (greedy)
\. Match the character “.” literally
\d Match a single digit 0..9
+ Between one and unlimited times, as many times as possible, giving back as needed (greedy)
\. Match the character “.” literally
( Match the regular expression below and capture its match into backreference number 2
\d Match a single digit 0..9
+ Between one and unlimited times, as many times as possible, giving back as needed (greedy)
)
$ Assert position at the end of the string (or before the line break at the end of the string, if any)
here is the solution:
$path4 ="D:\Updates3"
function search {
param (
[string]$Path,
[int]$M,
[int]$j
)
$Updates = (Get-ChildItem -Path $path4).BaseName
$g=foreach ($item in $Updates) {
[version]$item
[version]$a="$M.0.0.$j"
if ([version]$item -eq $a) {
Write-Host $a
}
}
}
search $path4 20 20
PS D:\powershell> d:\powershell\Untitled-1.ps1
20.0.0.20

How to select and fill row in form

I've powershell script which should basically fill the empty row in the picture. I'm not able to find solution on how to select that part and fill it with number.
I've tried to find the name with regex but didn't succeed
here is part of the code
$krokPattern = "https://kazdykrokpomaha.ozp.cz/index.php?kroky/index"
$ie.navigate($krokPattern)
while($ie.Busy) { Start-Sleep -Milliseconds 100 }
[regex]$regex = "krok-\d{4}-\d{2}-\d{2}"
$stering = Select-String -Path $krokPattern -Pattern $regex
Image - how it looks like
You can do something like the following with -replace. Just replace the value assigned to $number with whatever value you deem appropriate. However, a proper parser for the language in the file is going to be best.
$regex = [regex]'(?<=type=")[^"]+(?=" name="krok-\d{4}-\d{2}-\d{2}")'
$number = 24
(Get-Content index.html) -replace $regex,$number | Set-Content index.html
Explanation:
Since -replace uses regex matching, we can build off of your current idea. See the following for the $regex breakdown. The goal is to match all characters between the double quotes after type= and before name="krok-####-##-##".
(?<=): Positive Lookbehind
type=": matches the characters type=" literally
[^"]+: matches a single character that is not " one or more times (+).
`(?=): Positive Lookahead
" name="krok-\d{4}-\d{2}-\d{2}": matches literally "krok- followed by 4 digits, a literal -, 2 digits, a literal -, 2 digits, and a final ".
The characters that match $regex are replaced by $number.
See Regex Demo for example and deeper explanation.

Manipulating first character of each line in a file with .Replace()

Say I have a text file 123.txt
one,two,three
four,five,six
My goal is to capitalize the first character of each line by using Get-Culture. This is my attempt:
$str = gc C:\Users\Administrator\Desktop\123.txt
#Split each line into an array
$array = $str.split("`n")
for($i=0; $i -lt $array.Count; $i++) {
#Returns O and F:
$text = (Get-Culture).TextInfo.ToTitleCase($array[$i].Substring(0,1))
#Supposed to replace the first letter of each array with $text
$array[$i].Replace($array[$i].Substring(0,1), $text) >> .\Desktop\finish.txt
}
Result:
One,twO,three
Four,Five,six
I understand that .Replace() is replaces every occurrence of the current array, which is why I made sure that it's replacing ONLY the first character of the array with $array[$i].Substring(0,1), but this doesn't work.
Try the following:
Get-Content C:\Users\Administrator\Desktop\123.txt | ForEach-Object {
if ($_) {
$_.Substring(0, 1).ToUpper() + $_.Substring(1)
} else {
$_
}
} > .\Desktop\finish.txt
Get-Content reads the input file line by line and sends each line - stripped of its line terminator - through the pipeline.
ForEach-Object processes each line in the associated script block, in which $_ represents the line at hand:
if ($_) tests if the line is nonempty, i.e. if there's at least 1 character; if not, the else block simply passes the empty line through.
$_.Substring(0, 1).ToUpper() converts the line's 1st character to uppercase, implicitly using the current culture (with a single character, this is equivalent to applying Get-Culture).TextInfo.ToTitleCase()).
+ $_.Substring(1) appends the rest of the line.
Only > rater than >> is needed to write to the output file, because the entire pipeline's output is written at once.
The reason this is not working is because you are replacing the character...
$array[$i].Substring(0,1)
... but you are using the Replace method on the entire array element
$array[$i].Replace(...
Here the array element is a string, equal to a line of the input. So it will replace every occurrence of that character.
Get-Content (unless you use the -Raw parameter) by default returns the text as an array of strings. So you should be able to use this regex replace (I have used ToString().ToUpper() - nothing wrong with the Get-Culture method)
$str = gc C:\Users\Administrator\Desktop\123.txt
foreach($line in $str){
$line -replace '^\w', $line[0].ToString().ToUpper() >> .\Desktop\finish.txt
}
Regex explanation:
^ is an anchor. It specifies "the beginning of the string"
\w matches a word character - usually a-z, A-Z, 0-9
See mklement0's comments here for the more focused ^\p{Ll} and here for further explanation

Powershell search and replace part of a string

I have a file with the following content:
123
456
789
XYZ
ABC999XXXXXXX
I need to search the file for 3 numeric characters, and replace the first and third character based on user input. If the user inputs character 1 = 0 and character 3 = 9, I need to return.
029
059
089
I'm trying to do this with a simple search and replace, without creating a variable for each character. Also of note, I need to search for the criteria of 3 numbers, discarding the alpha character rows.
Please Note: This is a simplified version of what I need to do. The situation is long with many more fields but I hope that boiling this down will give me something that I can work with. Thanks in advance.
Assuming that the conditions are as you've described, the -replace operation suggested in the comment should do just what you want.
All you need to do is take user input and interpolate it into the replace string, exemplified below:
# Get user input for the first digit
do{
$a = Read-Host -Prompt "Input 1st digit"
} while ($a -notmatch "^\d$")
# Get user input for the third digit
do{
$b = Read-Host -Prompt "Input 3rd digit"
} while ($b -notmatch "^\d$")
# pattern that matches exactly 3 digits, captures the middle one
$pattern = "^\d(\d)\d$"
# replacement consisting of the user input and a reference to the capture group
$replace = "$a{0}$b" -f '${1}'
# Let's replace!
$InputObject = Get-Content "C:\my\file\path.txt"
$InputObject -replace $pattern,$replace