Powershell Select-String - powershell

I need your help with PowerShell.
I need Select-String with fixed Date (in variable). & Set-Content to result.txt
Example: $Date = "01.07.2020"
But also i need select string with date which lower than i written in variable.
My code: Get-Content -Path log.txt | Select-String "?????" | Set-Content $result.txt
In log.txt i have many strings like " Creation date 01.07.2020 " ; " Creation date 01.06.2020 "

123.txt
Creation date 01.07.2020
Creation date 02.05.2020
Creation date 01.06.2020
Creation date 28.08.2020
Example script
$file = "C:\Users\userprofile\Desktop\test\123.txt"
$regexpattern = "\d{2}\.\d{2}\.\d{4}"
$content = Get-Content $file | Where-object { $_ -match $regexpattern}
foreach($line in $content){
$line.Substring(13,11)
}
I used regex to find the lines you are wanting to output. We get the content only if it matches our regex, then for each line we found, I'm using substring to pull the date out. You could also put together a regex for this if you wanted to. Since we know the lines have the same number of characters it's safe to use the substring function.
If you want that output to a file, simply find $line.Substring(13,11) and then add this after it | Out-file "C:\Users\userprofile\desktop\test\output.txt" -append.

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'

Powershell change date format based on regex

I am trying to use a regex to find dates in a csv file and change the formatting because there are over 200 columns in this csv; manual column mapping for each date is not possible.
what I had previously was the following
$sf = '\\path\dept\Extracts\Date_Modified.csv'
$regex = "\d{1,2}/\d{1,2}/\d{4}"
(Get-Content $sf) |
Foreach-Object {$_ -replace $regex, (get-date -f "yyyy-MM-dd") } |
Set-Content $sf
that works fine if I want to replace all the dates with the current date, but that wasn't my goal. my goal is to recognize human entry type dates (mm/dd/yyyy) and change them to yyyy-mm-dd that the database table is expecting when I load the csv.
how can I modify this? or is there a better way to recognize date formats and change the format?
The answer: use capture groups. I don't know why you wouldn't be able to assign the regex to a variable before use (as I have done many times):
$sf = '\\path\dept\Extracts\Date_Modified.csv'
$regex = '(\d{1,2})\/(\d{1,2})\/(\d{4})'
#(Get-Content -Path $sf) |
ForEach-Object { $_ -replace $regex, '$3-$1-$2' } |
Set-Content -Path $sf
Of special note, use single-quotes in the replace statement so you don't end up trying to interpolate $1 into a (presumably null) variable.

Find and replace strings in files in a given date range by filename

A nice tough one for you all. I'm trying to find and replace a given string in a bunch of files. The files have a date stamp in the file name i.e. YYYY_MM_DD_file.txt
I wish to search and replace within a date range for these files and then replace a string I define, I cannot use date modified as the date range, I must rely on the stamp in the filename.
So far I set my date range in WPF text fields:
$Filename = $Filenamebox.text
$startdate = [datetime] $startdatetext.text
$enddate = [datetime] $enddatetext.Text
$NewFilenamereal = $Newfilename.Text
$array =
do {
$startdate.ToString('yyyy_MM_dd*')
$startdate = $startdate.AddDays(1)
}
until ($startdate -gt [datetime] $enddate)
$files1 = $array | foreach-object {"C:\Users\michael.lawton\Desktop\KGB\Test folder\$_"}
write-host $files1
I then get child items using the $files1 array I have created as a search mask for the files in the date range and find all matches. Store this in a variable and replace the string $filename with the new string $Newfilenamereal.
$Matches1 = get-childitem $files1 | select-string $Filename | foreach-object {$_ -replace $Filename,$Newfilenamereal} | out-string
write-host $Matches1
However I cannot work out how to overwrite what has been found and replaced in the $Matches1 variable to the original files. I have tried set-content, however this will simply either erase everything I have in the date stamped files or cannot understand the $files1 array as a file path.
So my question to you lovely people is how do I write what I have replaced in the environment to the actual files?
Just retrieve the file content using the Get-Content cmdlet and replace the string. Finally write it back using the Set-Content cmdlet:
Get-ChildItem $files1 | ForEach-Object {
($_ | Get-Content -Raw) -replace $Filename,$Newfilenamereal |
Set-Content -Path $_.FullName -Encoding UTF8
}

Merge Text Files and Prepend Filename and Line Number - Powershell

I've been trying to adapt the answer to this question: powershell - concatenate N text files and prepend filename to each line
My desired output based on an example of 2 .txt files:
First.txt
lines of
data
Second.txt
more lines
of
data
Output.txt
First1 lines of
First2 data
Second1 more lines
Second2 of
Second3 data
Based on the most similar question I could find the following answer:
Select-String '^' *.txt >output.txt
Would give:
C:\A\Filepath\First.txt:1:lines of
C:\A\Filepath\First.txt:2:data
C:\A\Filepath\Second.txt:1:more lines
C:\A\Filepath\Second.txt:2:of
C:\A\Filepath\Second.txt:3:data
So I was hoping to use -replace to remove the filepath, keep the file name (but remove .txt:), keep the line number (but replace the final : with a space) and keep the text from the line.
Any help would be appreciated reaching the desired output.txt. Thanks
Not beautiful but this is one approach.
Get-ChildItem *.txt |
%{$FILENAME=$_.BaseName;$COUNT=1;get-content $_ |
%{"$FILENAME"+"$COUNT"+" " + "$_";$COUNT++}}|
Out-File Output.txt
The select-string approach is very interesting. The way I would go about it is to use Get-Content. The advantage there is that each line has a readcount property that represents the line number.
Get-ChildItem "C:\temp\*.file" | ForEach-Object{
$fileName = $_.BaseName
Get-content $_ | ForEach-Object{
"{0}{1} {2}" -f $fileName,$_.ReadCount,$_
}
} | Add-Content "C:\temp\output.txt"
Take each file and use Get-Content. With each line we process we send to the output stream a formatted line matching your desired output. No need to count the lines as $_.ReadCount already knows.
Select-String still works
You just need to manipulate the output to match what you want. Using Get-Member we can check the properties of what select-string returns to get our desired output.
Select-String '^' "c:\temp\*.txt" | ForEach-Object{
"{0}{1} {2}" -f $_.Filename,$_.LineNumber,$_.Line
} | Add-Content "C:\temp\output.txt"

Export-CSV cmdlet rewriting entire CSV during each iteration of a FOREACH statement

I'm working with some code that is going to take a series of performance counters, and then put the counters in a .csv file that rolls over every time it hits 1MB.
$Folder="C:\Perflogs\BBCRMLogs" # Change the bit in the quotation marks to whatever directory you want the log file stored in
$Computer = $env:COMPUTERNAME
$1GBInBytes = 1GB
$p = LOTS OF COUNTERS;
# If you want to change the performance counters, change the above list. However, these are the recommended counters for a client machine.
$num = 0
$file = "$Folder\SQL_log_${num}.csv"
if( !(test-path $folder)) {New-Item $Folder -type directory}
Get-Counter -counter $p -SampleInterval 2 -Continuous | Foreach {
if ((Get-Item $file -ErrorAction SilentlyContinue ).Length -gt 1mb)
{
$num +=1
$file = "$Folder\SQL_log_${num}.csv"
}
$_
} | Foreach-Object { $_ | Export-Csv $file -Force -Append}
Right now, it's working quite well. The iteration works fine, and it does create a new file each time the .csv reaches 1MB. However, each .CSV after the first is being created after 2 minutes already at 1MB, causing a new file to be created. I'm not quite sure why this is occurring, although I believe it's because Powershell is just rewriting the entirety of the .csv each time it creates it.
[I'm posting this as a new answer rather than editing the original because it's completely different. Replacing or appending to the original answer would make the ensuing discussion confusing.]
What you need to do is use a regex to extract the values from the Readings property of the output of Get-Counter, and manually construct CSV output from the timestamp and those values. Change the last line to this (format according to your preferred style):
| %{'"' + (Get-Date $_.Timestamp -f 's') + '","' + (([regex]::matches($_.Readings, '(?<=\\\\.+?:\n)(.+?)(?=\n)') | select -ExpandProperty Value) -join '","') + '"'} | Out-File $file -Append -Encoding ASCII
To break that down:
(Get-Date $_.Timestamp -f 's') This part is not strictly necessary, though I think it will make your results easier to follow. The 's' format puts the date in an ISO 8601 sortable pattern. You could substitute 'u' for another sortable format, or use your favorite custom format string. Or just replace it with $_.Timestamp to retain the original format.
[regex]::matches($_.Readings, '(?<=\\\\.+?:\n)(.+?)(?=\n)') The regex matches the contents of any line that is preceded by a line that begins with \\ and ends with : (those pesky counter names you wanted to get rid of). Note that I'm using [regex]::matches, which performs a global match, as opposed to [regex]::match or -match, which will just give you the first match for each string (the Readings property is a single string, so only the first counter reading would be returned).
| select -ExpandProperty Value Produces an array of all the matches, which you can then join with "," and surround with "'s to produce CSV output.
Since you're not using a conversion function, you also need to construct a header row. Add this line right above the pipeline:
`'"Timestamp","' + ($p -join '","') + '"' | Out-File $file -Append -Encoding ASCII`
That's assuming that $p is an array (which it should be). If it's a string, then depending on the format you can either use it as-is, or -split it and rejoin it in CSV format.
Change the last line to this, to convert each line to CSV format and then append it to the output file:
} | Foreach-Object {($_ | ConvertTo-Csv -NoTypeInformation)[1] | Out-File $file -Append -Encoding ASCII}.
A few notes:
The -Encoding ASCII is not strictly necessary, but you might have trouble with a Unicode CSV file in some applications (Excel, for example, won't open it as a CSV file by default, and everything will be in Column A)
The reason for the index in ($_ | ConvertTo-Csv -NoTypeInformation)[1] is that ConvertTo-Csv -NoTypeInformation still outputs the header row each time, so you want to grab the second line of the two-line output (($_ | ConvertTo-Csv -NoTypeInformation)[0] is the header row)
Since you're not outputting a header row, you'll need to output one to $file before the loop