Remove attribute and reformat date string in a file - powershell

I'm able to remove the attribute using the codes below. However, I don't know how to reformat a date string to the ISO format. From date="20140424T140222Z" to date="2014-04-24T14:02:22Z"
function update {
$unzippedLocation = Get-ChildItem $destination -Recurse
# from date="20140424T140222Z" to date="2014-04-24T14:02:22Z"
Message "Remove and reformat attributes"
$regex='date="(\d{4})(\d{2})(\d{2})T(\d{2})(\d{2})(\d{2})Z"'
ForEach($unzippedFile in $unzippedLocation) {
(Get-Content $unzippedFile) |
ForEach-Object { $_ -replace ' crc=""', '' } |
ForEach-Object { $_ -replace $regex, 'date="$1-$2-$3T$4:$5:$6Z"' } |
Set-Content $unzippedFile
Write-Host "crc attribute has been removed from $($unzippedFile.Name)"
Write-Host "date attribute has been reformated from $($unzippedFile.Name)"
}
}

I don't fully understand where you need to that the replacement of the date, but one approach is to use regular expression to update the date. Here is a sample for one string, you can incorporate it where you need to:
#Defines regex with a separate group for each component
$regex='date="(\d{4})(\d{2})(\d{2})T(\d{2})(\d{2})(\d{2})Z"'
#Sample input
$input = 'date="20140424T140222Z"'
#Update the string
$result = $input -replace $regex, 'date="$1-$2-$3T$4:$5:$6Z"'
Write-Host $result
#Result is
#date="2014-04-24T14:02:22Z"

You can use the .net method TryParseExact.
This method will try to parse any string in a valid DateTime value and then you format back to ISO format. Using this method you test if your values are valid.
Example:
$dateString = "20140424T140222Z"
$format = "yyyyMMddTHHmmssZ"
[ref]$parsedDate = get-date
$parsed = [DateTime]::TryParseExact($dateString, $format,[System.Globalization.CultureInfo]::InvariantCulture,[System.Globalization.DateTimeStyles]::None,$parseddate)
if($parsed)
{
write "$dateString is valid"
}
$parseddate.Value.ToString("yyyy-MM-ddTHH:mm:ssZ")

Related

Powershell Files fetch

Am looking for some help to create a PowerShell script.
I have a folder where I have lots of files, I need only those file that has below two content inside it:
must have any matching string pattern as same as in file file1 (the content of file 1 is -IND 23042528525 or INDE 573626236 or DSE3523623 it can be more strings like this)
also have date inside the file in between 03152022 and 03312022 in the format mmddyyyy.
file could be old so nothing to do with creation time.
then save the result in csv containing the path of the file which fulfill above to conditions.
Currently am using the below command that only gives me the file which fulfilling the 1 condition.
$table = Get-Content C:\Users\username\Downloads\ISIN.txt
Get-ChildItem `
-Path E:\data\PROD\server\InOut\Backup\*.txt `
-Recurse |
Select-String -Pattern ($table)|
Export-Csv C:\Users\username\Downloads\File_Name.csv -NoTypeInformation
To test if a file contains a certain keyword from a range of keywords, you can use regex for that. If you also want to find at least one valid date in format 'MMddyyyy' in that file, you need to do some extra work.
Try below:
# read the keywords from the file. Ensure special characters are escaped and join them with '|' (regex 'OR')
$keywords = (Get-Content -Path 'C:\Users\username\Downloads\ISIN.txt' | ForEach-Object {[regex]::Escape($_)}) -join '|'
# create a regex to capture the date pattern (8 consecutive digits)
$dateRegex = [regex]'\b(\d{8})\b' # \b means word boundary
# and a datetime variable to test if a found date is valid
$testDate = Get-Date
# set two variables to the start and end date of your range (dates only, times set to 00:00:00)
$rangeStart = (Get-Date).AddDays(1).Date # tomorrow
$rangeEnd = [DateTime]::new($rangeStart.Year, $rangeStart.Month, 1).AddMonths(1).AddDays(-1) # end of the month
# find all .txt files and loop through. Capture the output in variable $result
$result = Get-ChildItem -Path 'E:\data\PROD\server\InOut\Backup'-Filter '*.txt'-File -Recurse |
ForEach-Object {
$content = Get-Content -Path $_.FullName -Raw
# first check if any of the keywords can be found
if ($content -match $keywords) {
# now check if a valid date pattern 'MMddyyyy' can be found as well
$dateFound = $false
$match = $dateRegex.Match($content)
while ($match.Success -and !$dateFound) {
# we found a matching pattern. Test if this is a valid date and if so
# set the $dateFound flag to $true and exit the while loop
if ([datetime]::TryParseExact($match.Groups[1].Value,
'MMddyyyy',[CultureInfo]::InvariantCulture,
[System.Globalization.DateTimeStyles]::None,
[ref]$testDate)) {
# check if the found date is in the set range
# this tests INCLUDING the start and end dates
$dateFound = ($testDate -ge $rangeStart -and $testDate -le $rangeEnd)
}
$match = $match.NextMatch()
}
# finally, if we also successfully found a date pattern, output the file
if ($dateFound) { $_.FullName }
elseif ($content -match '\bUNKNOWN\b') {
# here you output again, because unknown was found instead of a valid date in range
$_.FullName
}
}
}
# result is now either empty or a list of file fullnames
$result | set-content -Path 'C:\Users\username\Downloads\MatchedFiles.txt'

Exclude parameters taken from XML by regular expression (regex)

I have a parameter in XML files with a mask:
<id>ALL2-20210301-XXXXXX-XXXXX_X</id>
where ALL2 is always the same parameter, 20210301 is a date so changing every day, and XXXXXX-XXXXX_X is a variable parameter.
I want to parse XML and get 20210304 value (or other date) with regex - I don't need any other parameter from . How should regular expression value looks like?
code is:
[xml]$xml = Get-Content c:\buy\buy.xml
$date= $xml.buy.id[0]
if ($date -match "regex?") {
$date = $matches[0];
}
You don't need regex, you can just split on '-' and take the second element, so for example:
$result = ($date -split '-')[1]
It is likely faster than regex too (haven't measured).
Several alternative approaches
$date = 'ALL2-20210301-XXXXXX-XXXXX_X'
$date.Split('-')[1]
$date -split '-' | Select-Object -Index 1
$date -replace '^.+?-|-.+$'
$date -replace '.+(\d{8}).+','$1'
[regex]::Match($date,'\d{8}').Value
if($date -match '\d{8}'){$matches.0}

Get-Date less than date/string from text file

I have written some code that parses warranty information about a license from a website into a .txt file. My goal (and problem) is to compare the warranty expiry date from a .txt file with the current date, in order to know if warranty has expired.
I am a powershell beginner so my code might not be very logical, but this is what I have so far:
#the Invoke-WebRequest is up here which outputs into $output_file
#files
$output_file = ‘c:\warrantyinfo.txt’
$warranty_file = 'c:\warrantydate.txt'
#At this point all the warranty information is in $output_file
#With the code below I do a select string to get the information I want and
#get rid of everything else
$warrantyUntil = Get-Content $output_file
$warrantyUntil | Select-String '("toCustomerDate":)["]\d{4}\-(0?[1-
9]|1[012])\-(0?[1-9]|[12][0-9]|3[01])*' -AllMatches | Foreach-Object
{$_.Matches} | Select value > $warranty_file
#I now have "toCustomerDate":"yyyy-mm-dd in a new .txt file
#Below I try to grab just the date in the format yyyy-mm-dd in order to
#compare with todays date. I think this is where I go wrong.
$warrantyDate=Select-String -Path $warranty_file -Pattern "\d{4}\-(0?[1-
9]|1[012])\-(0?[1-9]|[12][0-9]|3[01])*" -AllMatches | Foreach-Object
{$_.Matches} | Select value
#Current date in the format I want
$currentDate=Get-Date -UFormat "%Y-%m-%d"
#Compare warrantyDate and currentDate to decide if warranty has expired or
#not
if ($currentDate -lt $warrantyDate) {
Write-Host "Warranty is valid"
} else {
Write-Host "Warranty has expired"
}
This is happens because of data type mismatch.
First of all your $currentDate is a string type, while you probably wanted it to be datetime type, this is happening because of performed specific formatting. Also $warrantyDate is string or even an array of strings. You need to take that in mind too.
You would want to explisitly set data type of initializtion variable by placing proper class acceleration before variable name. Like so:
[DateTime]$currentDate=Get-Date -UFormat "%Y-%m-%d"
[DateTime]$warrantyDate=Select-String -Path $warranty_file -Pattern "\d{4}\-(0?[1-
9]|1[012])\-(0?[1-9]|[12][0-9]|3[01])*" -AllMatches | Foreach-Object
{$_.Matches} | Select value
To see what is variable type use this:
$currentDate.GetType().Name
With a file warrantyinfo.txt:
"toCustomerDate":"2018-04-22"
"toCustomerDate":"2018-04-24"
and this script:
$currentDate=Get-Date
$output_file = ‘.\warrantyinfo.txt’
$warrantyUntil = Get-Content $output_file |
Select-String '(?<="toCustomerDate":)"\d{4}\-(0?[1-9]|1[012])\-(0?[1-9]|[12][0-9]|3[01])"' -AllMatches
$warrantyUntil
$warrantyUntil.matches.value
$warrantyUntil.matches.value | ForEach {
if ($currentDate -lt [datetime]$_.trim('"')) {
Write-Host "Warranty $_ is valid"
} else {
Write-Host "Warranty $_ has expired"
}
}
You'll get this sample output:
"toCustomerDate":"2018-04-22"
"toCustomerDate":"2018-04-24"
"2018-04-22"
"2018-04-24"
Warranty "2018-04-22" has expired
Warranty "2018-04-24" is valid
The script uses a different RegEx with a positive LookBehind assertion for (?<="toCustomerDate":) which isn't part of matched value.

Parse out date from filename and sort by date

I have a series of files named as such in a folder:
- myFile201801010703.file
I'm trying to parse out the yyyymmdd portion of each filename in the folder and sort them based on the date into an array.
So if I had the following files:
myFile201801200000.file (01/20/2018)
myFile201800100000.file (01/01/2018)
myFile201801100000.file (01/10/2018)
It would sort them into an array as such:
myFile201800100000.file (01/01/2018)
myFile201801100000.file (01/10/2018)
myFile201801200000.file (01/20/2018)
I have a process that works for file with timestamps included in the name, though have been unable to tweak it for work with only a date:
# RegEx pattern to parse the timestamps
$Pattern = '(\d{4})(\d{2})(\d{2})*\' + ".fileExtension"
$FilesList = New-Object System.Collections.ArrayList
$Temp = New-Object System.Collections.ArrayList
Get-ChildItem $SourceFolder | ForEach {
if ($_.Name -match $Pattern) {
Write-Verbose "Add $($_.Name)" -Verbose
$Date = $Matches[2],$Matches[3],$Matches[1] -join '/'
$Time = $Matches[4..6] -join ':'
[void]$Temp.Add(
(New-Object PSObject -Property #{
Date = [datetime]"$($Date) $($Time)" #If I comment out $($Time)it doesn't work.
File = $_
}
))
}
}
} catch {
Write-Host "`n*** $Error ***`n"
}
# Sort the files by the parsed timestamp and add to $FilesList
$FilesList.AddRange(#($Temp | Sort Date | Select -Expand File))
# Clear out the temp collection
$Temp.Clear()
The two lines in particular that I think might be culprit are:
$Time = $Matches[4..6] -join ':' Since I'm not parsing any time
Date = [datetime]"$($Date) $($Time)" Again, no time is parsed. Can't change the type to date either it seems?
With this format:
myFileYYYYMMddHHmm.file
the individual parts of the date and time is already arranged from largest (the year) to smallest (the minute) - this makes the string sortable!
Only thing we need to do is grab the last 12 digits of the file name before the extension:
$SortedArray = Get-ChildItem *.file |Sort-Object {$_.BaseName -replace '^.*(\d{12})$','$1'}
The regex pattern used:
^.*(\d{12})$
Can be broken down as follows:
^ # start of string
.* # any character, 0 or more times
( # capture group
\d{12} # any digit, 12 times
) # end of capture group
$ # end of string
The regex engine will expand $1 in the substitution string to "capture group #1", ie. the 12 digits we picked up at the end.

Loop over array

I need a piece of powershell-code to search and replace a certain string inside a text-file. In my example, I want to replace 23-06-2016' with '24-06-2016'. The script below does this job:
$original_file = 'file.old'
$destination_file = 'file.new'
(Get-Content $original_file) | Foreach-Object {
$_ -replace '23-06-2016', '24-06-2016' `
} | Out-File -encoding default $destination_file
As the search / replace string change I want to loop over an array of dates which might look like this:
$dates = #("23-06-2016","24-06-2016","27-06-2016")
I tried use the
$original_file = 'file.old'
$destination_file = 'file.new'
foreach ($date in $dates) {
(Get-Content $original_file) | Foreach-Object {
$_ -replace 'date', 'date++' `
} | Out-File -encoding default $destination_file
}
In a first step, the date '23-06-2016' should be replaced by '24-06-2016' and in a second step, the date '24-06-2016' should be replaced by '27-06-2016'.
As my script is not working I am seeking for some advice.
You are using $date as your instance variable in your foreach loop but then referencing it as 'date', which is just a string. Even if you used '$date' it would not work because single-quoted strings do not expand variables.
Further, $date is not a number, so date++ would not do anything even it were referenced as a variable $date++. Further still, $var++ returns the original value before incrementing, so you would be referencing the same date (as opposed to the prefix version ++$var).
In a foreach loop, it's not very practical to refer to other elements, in most cases.
Instead, you could use a for loop:
for ($i = 0; $i -lt $dates.Count ; $i++) {
$find = $dates[$i]
$rep = $dates[$i+1]
}
This isn't necessarily the most clear way to do it.
You might be better off with a [hashtable] that uses the date to find as a key, and the replacement date as the value. Sure, you'd be duplicating some dates as value and key, but I think I'd rather have the clarity:
$dates = #{
"23-06-2016" = "24-06-2016"
"24-06-2016" = "27-06-2016"
}
foreach ($pair in $dates.GetEnumerator()) {
(Get-Content $original_file) | Foreach-Object {
$_ -replace $pair.Key, $pair.Value
} | Out-File -encoding default $destination_file
}