Powershell change file - powershell

I am very new in powershell, I hope someone will help me:
I have a file in C:\tmp\appFiles.appcache
Which contains:
CACHE MANIFEST
Version: 2019-01-04T00:48:08.3070330+01:00
Use from network if available
NETWORK:
I need to make a script to change date + time to actual time
For example:
CACHE MANIFEST
Version: 2019-01-19T13:55:08.3070330+01:00
Use from network if available
NETWORK:
Thank you for any help.

Or use Select-String and the 'Round-trip date/time pattern' 'o'
See Standard Date and Time Format Strings
$path = 'C:\tmp\appFiles.appcache\YOUR-FILENAME'
$content = Get-Content $path
$match = $content | Select-String -Pattern 'Version:' -SimpleMatch -List
if ($match) {
$content[$match.LineNumber - 1] = "Version: {0}" -f (Get-Date -Format 'o')
$content | Set-Content -Path $path
}

This really is not a script writing service. But if you promise me you will learn more about PowerShell and start trying to understand the following script, I will give you this to work with. Protip: when learning PowerShell you should use PowerShell ISE and press cntrl + R so you have a better way to write and understand PowerShell.
$path = "path to your file"
$content = Get-Content $path
$date = Get-Date -Format s
foreach($row in $content){
if($row -match "Version:"){
$newContent = $content.replace("$row","Version: $date+01:00")
}
}
$newContent | Set-Content $path
The above script gets the content of the file given, then takes the current date. It then searches each row of the content for a mathing text. When found it replaces that specific line with the custom line we created. Then we take the new content and set it to the file we specified. You basically overwrite the whole file this way!

To have a complete date string according to the given format with part seconds and UTC offset
replace the Version string with a positive lookbehind RegEx
save to same file name (requires reading to memory first)
## Q:\Test\2019\01\18\SO_54255102.ps1
$File = 'C:\tmp\appFiles.appcache'
$Date = Get-Date -f 'yyyy-MM-dd\THH:mm:ss.fffffffzzzzz' # or simply (Get-Date -f o)
(Get-Content $File) -Replace '(?<=^Version: ).*$',$Date | Set-Content $File

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'

Preserve Emojis with Get-Content Powershell

I want to preserve Emojis with Get-Content.
When I pull the string from the feed I get the following result:
$WebResponse = Invoke-RestMethod $website
$str_outputNAME = $feed.title
Wanna try😉?
But when I save the content of the file and append it after I have the following result:
$content = (Get-Content -Path $file) -join "`n"
$toWrite = $top_line+$toWrite+$content
$toWrite | Out-File -FilePath $file;
Wanna try???
Background-Info
I want to use Powershell to read a rss-feed.
Therefor I need to append a string at the start of my CSV-File on update.
Because my question was regarding *.csv files I found that a better way is too use
$content = Import-Csv -Path $file
instead of
$content = Get-Content -Path $file
Now all my Emojis are preserved within the file but the processing of the script takes twice the time now.
I tried all different possible Get-Content -Encoding arguments but without luck.
Always resulted in loss towards the formatting of emojis.

Saving Find and Replace text function for .dat files in Powershell

I'm making a script that will find and replace all the instances of a word with another. However I'm unsure how to save the changes.
$file = Get-Content "C:\Script.dat" -Raw
$old = 'oldword'
$new = 'newword'
$file.Replace($old,$new)
Initially I had used the following but this caused issues.
$file.Replace($old,$new) | Set-Content $file
This caused the issue the error of
Set-Content : Cannot find drive. A drive with the same *some random stuff*...
How would I be able to save the changes and/or fix the above issue?
$file = Get-Content "C:\Script.dat" -Raw
$old = 'oldword'
$new = 'newword'
$file.Replace($old,$new) | Out-File -FilePath C:\Script.dat
You were very close, but Set-Content needs two things: a path to the file location and the value to store. Personally, I prefer to overwrite variables when using the .Replace() method instead of piping it into other cmdlets.
This will do it:
$file = Get-Content "C:\Script.dat" -Raw
$old = 'oldword'
$new = 'newword'
$file = $file.Replace($old,$new)
Set-Content -Path "C:\Script.dat" -Value $file
If possible, try to avoid storing files directly at C:\ since that often needs admin rights to write to.
Additionally, you could pipe to Set-Content in a similar way originally listed but you still need to give it the path to the file:
$file.Replace($old,$new) | Set-Content "C:\Script.dat"

PS1 Replace or Remove a single character without rewriting the whole file

Is it possible to remove or replace the last character on the last non-whitespace line of a file using PowerShell 1?
I'm trying to get an Uptime log that is precise to within 5 minutes.
I've found that there are built logs and commands that can be accessed through command prompt that would tell me when the last time a computer was booted up, or when it shut down correctly, but the native uptime log only records once every 24 hrs, so if there is a power failure, I won't know how long the system has been offline with any precision more refined than 24 hours.
So I have created the following script:
$bootTime = (Get-WmiObject Win32_OperatingSystem).LastBootUpTime
$formBootTime = [Management.ManagementDateTimeConverter]::ToDateTime($bootTime)
$uptime = (Get-Date)-$formBootTime
"$formBootTime,$(Get-Date),{0:00},{1:00},{2:00},{3:00}" -f $uptime.Days,$uptime.Hours,$uptime.Minutes,$uptime.Seconds >> C:\UptimeTracker.csv
However, this gets tediously long to scroll through when I want to evaluate how long my machine has been running over the last X days.
So I thought I would add a marker to identify the current or most recent Uptime log per any given Boot.
But in order for that to work I would need to be able to remove said marker as soon as the previous record is no longer the relevant record.
$bootTime = (Get-WmiObject Win32_OperatingSystem).LastBootUpTime
$formBootTime = [Management.ManagementDateTimeConverter]::ToDateTime($bootTime)
$file = (Get-Content c:\UptimeTracker.csv | Measure-Object)
$numberOfLines = $file.Count
$numberOfWords = (Get-Content c:\UptimeTracker.csv | Select -Index ($numberOfLines -1) | Measure-Object -word)
$Line = Get-Content c:\UptimeTracker.csv | Select -Index ($numberOfLines -2)
$wordArray = $Line.Split(",")
$LastLineBT = $wordArray[0]
if($LastLineBT -eq $formBootTime) {
$unmark = "true"
}
else
{$unmark = "false"}
if($unmark == "true"){ <remove last character of file> }
$uptime = (Get-Date)-$formBootTime
"$formBootTime,$(Get-Date),{0:00},{1:00},{2:00},{3:00},X" -f $uptime.Days,$uptime.Hours,$uptime.Minutes, $uptime.Seconds >> C:\UptimeTracker.csv
Some of the above is borrowed and modified from: https://stackoverflow.com/a/16210970/11035837
I have seen several methods that receive the file as the input file and write to a different output file, and from there it would be an easy thing to do to script renaming the new and old files to switch their positions (new, old, standby - and rotate) the reason I'm trying not to rewrite the whole file is to reduce those instances where the command/script is interrupted and the action doesn't complete. Ideally the only time the action doesn't complete would be on a power failure. However, I have already seen in a previous version, it would skip 5 minute intervals occasionally for up to 15 minutes without any change in the last reported boot time. I suspect this has to do with other higher priority processes preventing the task scheduler from running the script. If this is the case, then a complete rewrite of the file failing part way through the script would lose some percentage of the existing log data, and I would rather miss the latest record than all the data.
Nothing I have found indicates any ability to remove/replace the last character (or two since one is a newline char), neither have I found anything that explicitly declares this is not possible - I have found declarations that it is not possible to elective replace inner or beginning content without a complete rewrite.
Barring any solution definitive answer, or if the definitive answer is no this cannot be done, then I will attempt something like the following:
if($unmark == "true"){
$input = "C:\UptimeTracker_CUR.csv"
$output = "C:\UptimeTracker_NEW.csv"
$content = Get-Content $input
$content[-2] = $content[-2] -replace 'X', ' '
$content | Set-Content $output
Rename-Item -Path "C:\UptimeTracker_CUR.csv" -NewName "C:\UptimeTracker_SBY.csv"
Rename-Item -Path "C:\UptimeTracker_NEW.csv" -NewName "C:\UptimeTracker_CUR.csv"
}
EDIT - due to multi-read comment by TheMadTechnician
...
$file = Get-Content c:\UptimeTracker.csv
$fileMeasure = ($file | Measure-Object)
$numberOfLines = $fileMeasure.Count
$numberOfWords = ($file | Select -Index ($numberOfLines -1) | Measure-Object -word)
$Line = $file | Select -Index ($numberOfLines -2)
...
...
if($unmark == "true"){
$output = "C:\UptimeTracker_NEW.csv"
$file[-2] = $file[-2] -replace 'X', ' '
$file | Set-Content $output
Rename-Item -Path "C:\UptimeTracker.csv" -NewName "C:\UptimeTracker_SBY.csv"
Rename-Item -Path "C:\UptimeTracker_NEW.csv" -NewName "C:\UptimeTracker.csv"
}
You read the whole file in several times, which has got to be slowing the whole script down. I would suggest reading the whole file in, determining if you need to clear your flag, then do so when you output, adding your new line to the file. Assuming you aren't still running PowerShell v2, you can do this:
$bootTime = (Get-WmiObject Win32_OperatingSystem).LastBootUpTime
$formBootTime = [Management.ManagementDateTimeConverter]::ToDateTime($bootTime)
$uptime = (Get-Date)-$formBootTime
$File = Get-Content c:\UptimeTracker.csv -raw
if($File.trim().split("`n")[-1].Split(',')[0] -eq $formBootTime){
$File.trim() -replace 'X(?=\s*$)',' '
}else{
$File.Trim()
},("$formBootTime,$(Get-Date),{0:00},{1:00},{2:00},{3:00},X" -f $uptime.Days,$uptime.Hours,$uptime.Minutes, $uptime.Seconds)|Set-Content c:\UptimeTracker.csv
If you are running an old version you will not have the -raw option for Get-Content. As a work around you can do this instead, and the same solution should still work.
$bootTime = (Get-WmiObject Win32_OperatingSystem).LastBootUpTime
$formBootTime = [Management.ManagementDateTimeConverter]::ToDateTime($bootTime)
$uptime = (Get-Date)-$formBootTime
$File = (Get-Content c:\UptimeTracker.csv) -Join "`n"
if($File.trim().split("`n")[-1].Split(',')[0] -eq $formBootTime){
$File.trim() -replace 'X(?=\s*$)',' '
}else{
$File.Trim()
},("$formBootTime,$(Get-Date),{0:00},{1:00},{2:00},{3:00},X" -f $uptime.Days,$uptime.Hours,$uptime.Minutes, $uptime.Seconds)|Set-Content c:\UptimeTracker.csv
This is going to be slower, so should be considered a secondary option since you'll have to read the whole file in as an array of strings, and convert it to a single multi-line string.

Loop to replace c:\, d:\ ... z:\ with \\servername\c$\

I'm actually trying to build some code to identify rights on shared folders in every single server I've got in my enterprise.
For now, I've already listed every single server and exported it in a .txt file, did a loop on this .txt to export in an other .txt file all shared folders.
All this is working fine but the path is like : c:\...\...\folder$.
To be able to use this I need to do a loop to replace c:\ d:\ etc. with \\servername\c$\.
I've tried using [system.io.file]::ReadAllText and WriteAllText, it's working fine for one letter but didn't find a way to do a loop on it.
I've tried
get-content ... -replace "c:\","\\$ServerName\c$\" ` -replace "d:\" ...
but got an error about regular expression not valid, so trying with [regex]::Escape but didn't work as expected neither...
Powershell
$contenu = [System.IO.File]::ReadAllText("$path\$SharedFolders.txt").Replace("C:\","\\$SharedFolders\c$\")
[System.IO.File]::WriteAllText("$path\$SharedFolders.txt", $contenu)
Powershell
(Get-Content "$path\$SharedFolders.txt") | foreach {
$_ -replace "C:\","\\$SharedFolders\C$\" `
-replace "D:\","\\$SharedFolders\D$\" `
[...] | Set-Content "$path\$sharedfolders.txt"}
And i'd like to have something like that :
Powershell
('a'..'z').ForEach({ (Get-Content "$path\$SharedFolders.txt" -Raw).replace("$_`:\","\\$SharedFolders\$_$") })
But I'm too newbie in Powershell to make it work proprely
You need PSv6 to use 'a'..'z'
The -replace operator is RegEx based, you need to escape a literal backslash with another one in the pattern.
following #Lee_Daileys hint build a RegEx with valid Drive letters
$OFS = '|'
$RE = ('('+(Get-Psdrive -PSProvider filesystem).where({$_.Displayroot -notmatch '^\\'}).name)+'):\\'
$OFS = $Null
"`$RE = '{0}'" -f $RE
'Loop to replace c:\, d:\ … z:\ with \\servername\c$\' -replace $RE,"\\servername\`${1}$\"
Sample output on my PC
$RE = '(A|C|D):\\'
Loop to replace \\servername\c$\, \\servername\d$\ … z:\ with \\servername\c$\
Reading the file with the -raw parameter doesn't require a loop, but wil do all changes at once.
$OFS = '|'
$RE = ('('+(Get-Psdrive -PSProvider filesystem).where({$_.Displayroot -notmatch '^\\'}).name)+'):\\'
$OFS = $Null
$File = "$path\$SharedFolders.txt"
(Get-Content $File -raw) -replace $RE,"\\servername\`${1}$\" |
Set-Content $File
Well thanks for your help, I just manage to make it works like that :
$lecteur=[int][char]'A'
1..26 | % {
$LR=[char]$lecteur
$contenu =[System.IO.File]::ReadAllText("$path\$SharedFolders.txt").Replace("${LR}:\","\\$SharedFolders\$LR$\")
[System.IO.File]::WriteAllText("$path\$SharedFolders.txt", $contenu)
$lecteur++
}
Hope it'll help some people ;)