Powershell Regex date conversion - regex

any idea why the following isnt working?
.................................................................................................................................................................................................................................................................................
$files = #'
name 14122012 text.doc
things 08092003.docx
hi v03.03 text 05062007 file.txt
hello world 31052006.ppt
names v04 12122012.xml
sdsf 29082013 dsf.php
'# -split '\n'
foreach ($File in $files) {
$file -match '(0[1-9]|[12][0-9]|3[01])(0[1-9]|1[012])(19|20)[0-9]{2}' | Out-Null
$File -replace "$(($matches).values)" , "$(get-date "$(($matches).Values)" -Format yyyyddMM)"
}
powershell output error is, for some reason its trying to add "20 12 14" to the string for conversion :S
Get-Date : Cannot bind parameter 'Date'. Cannot convert value "20 12 14 14122012" to type "System.DateTime". Error: "String was not recognized as a valid DateTime."
At line:13 char:9
+ get-date <<<< "$(($matches).Values)" -Format yyyyddMM
+ CategoryInfo : InvalidArgument: (:) [Get-Date], ParameterBindingException
+ FullyQualifiedErrorId : CannotConvertArgumentNoMessage,Microsoft.PowerShell.Commands.GetDateCommand
and
$files = #'
11.12.2012
11.12.12
15 12 2013
5 06 2013
'# -split '\n'
foreach ($File in $files) {
$file -match '\d{2}\.\d{2}\.\d{2,4}' | Out-Null
$file -match '(0[1-9]|[12]\d|3[01])\s\d{2}\s\d{2,4}'| Out-Null
$File -replace "$(($matches).values)" , "$(get-date "$(($matches).Values)" -Format yyyyMMdd)"
}
15 12 2013
Get-Date : Cannot bind parameter 'Date'. Cannot convert value "15 15 12 2013" to type "System.DateTime". Error: "String was not recognized as a valid DateTime."
At line:13 char:9
+ get-date <<<< "$(($matches).Values)" -Format yyyyMMdd
+ CategoryInfo : InvalidArgument: (:) [Get-Date], ParameterBindingException
+ FullyQualifiedErrorId : CannotConvertArgumentNoMessage,Microsoft.PowerShell.Commands.GetDateCommand
5 06 2013
Get-Date : Cannot bind parameter 'Date'. Cannot convert value "15 15 12 2013" to type "System.DateTime". Error: "String was not recognized as a valid DateTime."
At line:13 char:9
+ get-date <<<< "$(($matches).Values)" -Format yyyyMMdd
+ CategoryInfo : InvalidArgument: (:) [Get-Date], ParameterBindingException
+ FullyQualifiedErrorId : CannotConvertArgumentNoMessage,Microsoft.PowerShell.Commands.GetDateCommand

It's doing what it's supposed to do. $matches is a hash table of the the base capture (0) and each capture group:
$text = 'name 14122012 text.doc'
$regex = $file -match '(0[1-9]|[12][0-9]|3[01])(0[1-9]|1[012])(19|20)[0-9]{2}' | Out-Null
$matches
Name Value
---- -----
3 2013
2 08
1 29
0 29082013
When you ouput the collection of values as a string, it's going to space separate them:
"$(($matches).values)"
2013 08 29 29082013
That's the normal behaviour for any collection that's converted to a string. You can change the separator character from the default space by changing the Output Field Separator ($OFS)
$OFS = ';'
"$(($matches).values)"
2013;08;29;29082013
That being said, you really don't need to jump through the datetime conversion hoops. You have all the data you need already:
$files = #'
name 14122012 text.doc
things 08092003.docx
hi v03.03 text 05062007 file.txt
hello world 31052006.ppt
names v04 12122012.xml
sdsf 29082013 dsf.php
'# -split '\n'
foreach ($File in $files) {
$file -match '(0[1-9]|[12][0-9]|3[01])(0[1-9]|1[012])((?:19|20)[0-9]{2})' | Out-Null
$File -replace "$($matches[0])" , ($matches[3,2,1] -join '')
}
name 20121214 text.doc
things 20030908.docx
hi v03.03 text 20070605 file.txt
hello world 20060531.ppt
names v04 20121212.xml
sdsf 20130829 dsf.php
It just took a minor re-factoring of the regex to capture all of the year digits, then arrange the captures in the right order and join them together.

Your first problem is that you weren't capturing the entire year (only the first 2 digits), so you need to move the group capture bracket:
$file -match '(0[1-9]|[12][0-9]|3[01])(0[1-9]|1[012])(19|20[0-9]{2})'
Also, $matches returns an array that contains the entire matched string in the first element, followed by an element for each captured group.
For example:
"name 14122012 text.doc" -match '(0[1-9]|[12][0-9]|3[01])(0[1-9]|1[012])(19|20[0-9]{2})'
$matches
Name Value
---- -----
3 2012
2 12
1 14
0 14122012
So you have to recombine the string in a format the get-date will like. In my locale, it would look like this:
$matches[2,1,3] -join " "
12 14 2012
If your locale needs the day of month first, then:
$matches[1..3] -join " "
14 12 2012
which then gives:
$File -replace "$($matches[0])" , "$(get-date $($matches[2,1,3] -join ' ') -Format yyyyddMM)"
for your second line.

Related

I want to split a string from : to \n in Powershell script

I am using a config file that contains some information as shown below.
User1:xyz#gmail.com
User1_Role:Admin
NAME:sdfdsfu4343-234324-ffsdf-34324d-dsfhdjhfd943
ID:xyz#abc-demo-test-abc-mssql
Password:rewrfsdv34354*fds*vdfg435434
I want to split each value from*: to newline* in my Powershell script.
I am using -split '[: \n]' it matches perfectly until there is no '' in the value. If there is an '*' it will fetch till that. For example, for Password, it matches only rewrfsdv34354. Here is my code:
$i = 0
foreach ($keyOrValue in $Contents -split '[: *\n]') {
if ($i++ % 2 -eq 0) {
$varName = $keyOrValue
}
else {
Set-Variable $varName $keyOrValue
}
}
I need to match all the chars after : to \n. Please share your ideas.
It's probably best to perform two separate splits here, it makes things easier to work out if the code is going wrong for some reason, although the $i % 2 -eq 0 part is a neat way to pick up key/value.
I would go for this:
# Split the Contents variable by newline first
foreach ($line in $Contents -split '[\n]') {
# Now split each line by colon
$keyOrValue = $line -split ':'
# Then set the variables based on the parts of the colon-split
Set-Variable $keyOrValue[0] $keyOrValue[1]
}
You could also convert to a hashmap and go from there, e.g.:
$h = #{}
gc config.txt | % { $key, $value = $_ -split ' *: *'; $h[$key] = $value }
Or with ConvertFrom-StringData:
$h = (gc -raw dims.txt) -replace ':','=' | ConvertFrom-StringData
Now you have convenient access to keys and values, e.g.:
$h
Output:
Name Value
---- -----
Password rewrfsdv34354*fds*vdfg435434
User1 xyz#gmail.com
ID xyz#abc-demo-test-abc-mssql
NAME sdfdsfu4343-234324-ffsdf-34324d-dsfhdjhfd943
User1_Role Admin
Or only keys:
$h.keys
Output:
Password
User1
ID
NAME
User1_Role
Or only values:
$h.values
Output:
rewrfsdv34354*fds*vdfg435434
xyz#gmail.com
xyz#abc-demo-test-abc-mssql
sdfdsfu4343-234324-ffsdf-34324d-dsfhdjhfd943
Admin
Or specific values:
$h['user1'] + ", " + $h['user1_role']
Output:
xyz#gmail.com, Admin
etc.

PowerShell Regex with csv file

I'm currently trying to match a pattern of IDs and replace with 0 or 1.
example pc0045601234 replace with 1234 the last 4 and add the 3rd digit in front "01234"
I tried the code below but the out only filled the userid column with No matching employee
$reportPath = '.\report.csv'`$reportPath = '.\report.csv'`
$csvPath = '.\output.csv'
$data = Import-Csv -Path $reportPath
$output = #()
foreach ($row in $data) {
$table = "" | Select ID,FirstName,LastName,userid
$table.ID = $row.ID
$table.FirstName = $row.FirstName
$table.LastName = $row.LastName
switch -Wildcard ($row.ID)
{
{$row.ID -match 'P\d\d\d\d\d\D\D\D'} {$table.userid = "Contractor"; continue}
{$row.ID -match 'SEC\d\d\d\D\D\D\D'} {$table.userid = "Contractor"; continue}
{$row.ID.StartsWith("P005700477")} {$table.userid = $row.ID -replace "P005700477","0477"; continue}
{$row.ID.StartsWith("P00570")} {$table.userid = $row.ID -replace "P00570","0"; continue}
default {$table.userid = "No Matching Employee"}
}
$output += $table
}
$output | Export-csv -NoTypeInformation -Path $csvPath
Here are three different ways to achieve the desired result. The first two use the same technique, just written in a different way.
First we put the sample data in a variable as a multiline string array. This is the equivalent as $text = Get-Content $somefile
$text = #'
PC05601234
PC15601234
'# -split [environment]::NewLine
Option 1 # convert to character array, select the 3rd and last 4 digits.
$text | foreach {-join ($_.ToChararray()| select -Skip 2 -First 1 -Last 4)}
Option 2 # same as above, requiring an extra -join to avoid spaces.
$text | foreach {(-join $_.ToChararray()| foreach{$_[2]+(-join $_[-4..-1])})}
Option 3 # my preference, regex. Capture the desired digits and replace the entire string with those two captured values.
$text -replace '^\D+(?!=\d)(\d)\w+([\d]{4}$)','$1$2'
All of these output
01234
11234
Further testing with different char/digit combinations and lengths.
$text = #'
PC05601234
PC15601234
PC0ABC124321
PC1DE4321
PC0A5678
PC1ABCD215678
'# -split [environment]::NewLine
Running the new sample data through each option all produce this output
01234
11234
04321
14321
05678
15678

Reading list style text file into powershell array

I am provided a list of string blocks in a text file, and i need this to be in an array in powershell.
The list looks like this
a:1
b:2
c:3
d:
e:5
[blank line]
a:10
b:20
c:30
d:
e:50
[blank line]
...
and i want this in a powershell array to further work with it.
Im using
$output = #()
Get-Content ".\Input.txt" | ForEach-Object {
$splitline = ($_).Split(":")
if($splitline.Count -eq 2) {
if($splitline[0] -eq "a") {
#Write-Output "New Block starting"
$output += ($string)
$string = "$($splitline[1])"
} else {
$string += ",$($splitline[1])"
}
}
}
Write-Host $output -ForegroundColor Green
$output | Export-Csv ".\Output.csv" -NoTypeInformation
$output | Out-File ".\Output.txt"
But this whole thing feels quite cumbersome and the output is not a csv file, which at this point is i think because of the way i use the array. Out-File does produce a file that contains rows that are separated by commas.
Maybe someone can give me a push in the right direction.
Thx
x
One solution is to convert your data to an array of hash tables that can be read into a custom object. Then the output array object can be exported, formatted, or read as required.
$hashtables = (Get-Content Input.txt) -replace '(.*?):','$1=' | ConvertFrom-StringData
$ObjectShell = "" | Select-Object ($hashtable.keys | Select-Object -Unique)
$output = foreach ($hashtable in $hashtable) {
$obj = $ObjectShell.psobject.Copy()
foreach ($n in $hashtable.GetEnumerator()) {
$obj.($n.key) = $n.value
}
$obj
}
$output
$output | Export-Csv Output.csv -NoTypeInformation
Explanation:
The first colons (:) on each line are replaced with =. That enables ConvertFrom-StringData to create an array of hash tables with values on the LHS of the = being the keys and values on the RHS of the = being the values. If you know there is only one : on each line, you can make the -replace operation simpler.
$ObjectShell is just an object with all of the properties your data presents. You need all of your properties present for each line of data whether or not you assign values to them. Otherwise, your CSV output or table view within the console will have issues.
The first foreach iterates through the $hashtables array. Then we need to enumerate through each hash table to find the keys and values, which is performed by the second foreach loop. Each key/value pair is stored as a copy of $ObjectShell. The .psobject.Copy() method is used to prevent references to the original object. Updating data that is a reference will update the data of the original object.
$output contains the array of objects of all processed data.
Usability of output:
# Console Output
$output | format-table
a b c d e
- - - - -
1
2
3
5
10
20
30
50
# Convert to CSV
$output | ConvertTo-Csv -NoTypeInformation
"a","b","c","d","e"
"1",,,,
,"2",,,
,,"3",,
,,,"",
,,,,"5"
,,,,
"10",,,,
,"20",,,
,,"30",,
,,,"",
,,,,"50"
# Accessing Properties
$output.b
2
20
$output[0],$output[1]
a : 1
b :
c :
d :
e :
a :
b : 2
c :
d :
e :
Alternative Conversion:
$output = ((Get-Content Input.txt -raw) -split "(?m)^\r?\n") | Foreach-Object {
$data = $_ -replace "(.*?):(.*?)(\r?\n)",'"$1":"$2",$3'
$data = $data.Remove($data.LastIndexOf(','),1)
("{1}`r`n{0}`r`n{2}" -f $data,'{','}') | ConvertFrom-Json
}
$output | ConvertTo-Csv -NoType
Alternative Explanation:
Since ConvertFrom-StringData does not guarantee hash table key order, this alternative readies the file for a JSON conversion. This will maintain the property order listed in the file provided each group's order is the same. Otherwise, the property order of the first group will be respected.
All properties and their respective values are divided by the first : character on each line. The property and value are each surrounded by double quotes. Each property line is separated by a ,. Then finally the opening { and closing } are added. The resulting JSON-formatted string is converted to a custom object.
You can split by \n newline, see example:
$text = #"
a:1
b:2
c:3
d:
e:5
a:10
b:20
c:30
d:
e:50
e:50
e:50
e:50
"#
$Array = $text -split '\n' | ? {$_}
$Array.Count
15
if you want to exclude the empty lines, add ? {$_}
With your example:
$Array = (Get-Content ".\Input.txt") -split '\n' | ? {$_}

PowerShell regex to pick out elapsed time

I am new to powershell scripting and am not so good with Regex...
I want to create a regular expression that will pick out time from the following text file...
gfskf dakdshadk daksdkdahkd daksdhasdkh () zadf sflh f.d / sd lhlfhlj f 12hrs:10mins:05sec fsfsf fsfjhsfjh
I want to get the hours so 12 and mins as 10 and seconds as 5.
$hour= [regex]::match($line,$hour_regex).Groups[1].Value
$mins= [regex]::match($line,$mins_regex).Groups[1].Value
$sec= [regex]::match($line,$sec_regex).Groups[1].Value
So essentially I need three regular expressions to extract the relevant data from the file.
Thanks in advance!
Use one regex:
$r = '(\d+)hrs:(\d+)mins:(\d+)sec'
$i = 'hlfhlj f 12hrs:10mins:05sec fsfsf f'
$result = [regex]::match($i,$r)
$hour = $result.Groups[1].Value
$mins = $result.Groups[2].Value
$sec = $result.Groups[3].Value
Just for fun:
$a = "gfskf dakdshadk daksdkdahkd daksdhasdkh () zadf sflh f.d / sd lhlfhlj f 12hrs:10mins:05sec fsfsf fsfjhsfjh"
$hour,$min, $sec = $a -split '(\d\d)' | ? { $_ -match '\d\d' }
Since nobody mentioned it yet, you can also use the -match operator. The submatches can be accessed via the $matches hashtable:
Get-Content "C:\path\to\your.txt" | ? {
$_ -match '(\d+)hrs:(\d+)mins:(\d+)sec'
} | % {
$h = $matches[1]
$m = $matches[2]
$s = $matches[3]
"{0}:{1}:{2}" -f ($h, $m, $s)
}
Use regex capture groups. "(\d+)hrs:(\d+)mins:(\d+)sec" will capture the numbers into groups that can be accessed. Save the results in a MatchCollection to check the results. Like so,
$line = "sflh f.d / sd lhlfhlj f 12hrs:10mins:05sec fsfsf"
PS C:\> $mc = [regex]::match($line, "(\d+)hrs:(\d+)mins:(\d+)sec")
PS C:\> $mc.Groups[1].value
12
PS C:\> $mc.Groups[2].value
10
PS C:\> $mc.Groups[3].value
05
You can try :
$b= [regex]".* (\d*)hrs:(\d*)mins:(\d*)sec.*"
$hours=$b.Match($a).groups[1].value
$minutes=$b.Match($a).groups[2].value
$seconds=$b.Match($a).groups[3].value
if like me you are not at ease with regex you can use split function :
PS>$t="gfskf dakdshadk daksdkdahkd daksdhasdkh () zadf sflh f.d / sd lhlfhlj f 12hrs:10mins:05sec fsfsf fsfjhsfjh"
PS>$slices=$t.split(#(" ",":"))
$hours=($slices[12]).substring(0,2)
$mins=($slices[13]).substring(0,2)
$secs=($slices[14]).substring(0,2)
PS> $s -replace '^.+ (\d+hrs:\d+mins:\d+sec).*$','$1' -split '\D+' | where {$_}
12
10
05

Import-Csv with regex

I want to import a CSV and filter out the data with certain regex's. Example of input CSV:
John;Doe;18
Peter;Test;21
I want to filter out individual names, for example: I only want to see the persons with the lastname starting with a 'd' and those who are 20 years and above.
How can I write the filtered data into a new array? Here's the piece of code I've written so far:
$persons = import-csv -Header "surname", "lastname", "age" -delimiter ";" data.csv
$persons | ForEach-Object {
if $_.lastname -match "^d" {
#?
}
Thanks very much
Try the following:
$persons = Import-Csv -Header "surname", "lastname", "age" -Delimiter ";" data.csv | where {
$_.lastname -match '^d' -and $_.age -ge 20
}
It filters out the records with lastname starting with "d" and have an age of 20 or greater. Finally, it saves the results in an array called $persons
$persons | Where-Object {$_.LastName -like "d*" -and $_.Age -gt 20}
try this regular expression
^([^;]*);([^;]*);([^;]*)$
Tried it with perl
#!/usr/bin/perl
use strict;
while (<>) {
if ( /^([^;]*);([^;]*);([^;]*)$/ ){
print "first =". $1 . "\n";
print "last =". $2 . "\n";
print "age =". $3 . "\n";
}
}
which gives this output
first =John
last =Doe
age =18
first =Peter
last =Test
age =21
however focus on the regular expression