I wrote this script to find all of the folders in a directory and for each folder, check inside a common file if some strings exist and if not add them. I needed to insert strings in particular places. Not really knowing how to do this, I opted for simpler find and replace where the strings needed to be inserted. Anyway this script takes almost an hour to work through 800 files. I'm hoping some experienced members can point out ways to make my task quicker as I have only been working with Powershell for two days. Many Thanks!!!
# First find and replace items.
$FindOne =
$ReplaceOneA =
$ReplaceOneB =
$ReplaceOneC =
# Second find and replace items.
$FindTwo =
$ReplaceTwo =
# Strings to test if exist.
# To avoid duplicate entries.
$PatternOne =
$PatternTwo =
$PatternThree =
$PatternFour =
# Gets window folder names.
$FilePath = "$ProjectPath\$Station\WINDOW"
$Folders = Get-ChildItem $FilePath | Where-Object {$_.mode -match "d"}
# Adds folder names to an array.
$FolderName = #()
$Folders | ForEach-Object { $FolderName += $_.name }
# Adds code to each builder file.
ForEach ($Name in $FolderName) {
$File = "$FilePath\$Name\main.xaml"
$Test = Test-Path $File
# First tests if file exists. If not, no action.
If ($Test -eq $True) {
$StringOne = Select-String -pattern $PatternOne -path $File
$StringTwo = Select-String -pattern $PatternTwo -path $File
$StringThree = Select-String -pattern $PatternThree -path $File
$StringFour = Select-String -pattern $PatternFour -path $File
$Content = Get-Content $File
# If namespaces or object don't exist, add them.
If ($StringOne -eq $null) {
$Content = $Content -Replace $FindOne, $ReplaceOneA
}
If ($StringTwo -eq $null) {
$Content = $Content -Replace $FindOne, $ReplaceOneB
}
If ($StringThree -eq $null) {
$Content = $Content -Replace $FindOne, $ReplaceOneC
}
If ($StringFour -eq $null) {
$Content = $Content -Replace $FindTwo, $ReplaceTwo
}
$Content | Set-Content $File
}
}
# End of program.
You could try writing to the file with a stream, like this
$stream = [System.IO.StreamWriter] $File
$stream.WriteLine($content)
$stream.close()
Related
I've been messing with this powershell script (i installed powershell on my mac OS) I also modified the code a bit in the first line.
I am not getting any errors, just nothing happens.
$folder = “/Users/mbp/Desktop/nier_unpacked_2_extracted“
$files = gci -recurse $folder | where { ! $_.PSIsContainer }
$fileContents = $files | foreach { gc -encoding utf8 $_.fullname }
$lines = $fileContents | foreach { if ($_ -match "^JP: (.*)$") { $matches[1] } }
$chars = $lines | foreach { $_.ToCharArray() }
$groups = $chars | group-object
$totals = $groups | sort-object -desc -property count
Basically outputting japanese text characters and how often they show up.
This is the original code(before modification):
$folder = "F:\nier_unpacked_2_extracted"
$files = gci -recurse $folder | where { ! $_.PSIsContainer }
$fileContents = $files | foreach { gc -encoding utf8 $_.fullname }
$lines = $fileContents | foreach { if ($_ -match "^JP: (.*)$") { $matches[1] } }
$chars = $lines | foreach { $_.ToCharArray() }
$groups = $chars | group-object
$totals = $groups | sort-object -desc -property count
Here is the link to the resource i got the code from if that helps: https://dev.to/nyctef/extracting-game-text-from-nier-automata-1gm0
I'm not sure why nothing is returning unfortunately.
In PowerShell (as in most other programming languages), $totals = ... means that you assign the result of the expression at the right side is assigned to the variable ($totals) at the left side.
To display the contents of the variable ($totals), you might use the Write-Output $totals, Write-Host $totals, Out-Defualt $totals, along with a lot of other output cmdlets.
Anyways, in PowerShell, it is generally not necessary to use a cmdlet in instances where the output is displayed by default. For example:
$totals Enter
I have a directory of .txt files that look like this:
[LINETYPE]S[STARTTIME]00:00:00
[LINETYPE]P[STARTTIME]00:00:00
[LINETYPE]B[STARTTIME]00:59:00
[LINETYPE]C[STARTTIME]00:59:00
[LINETYPE]C[STARTTIME]00:59:30
[LINETYPE]S[STARTTIME]01:00:00
[LINETYPE]P[STARTTIME]01:00:00
[LINETYPE]B[STARTTIME]01:59:00
[LINETYPE]C[STARTTIME]01:59:00
[LINETYPE]C[STARTTIME]01:59:30
[LINETYPE]S[STARTTIME]02:00:00
I'd like to remove all occurrences of [LINETYPE]S except the first, which happens to always be 00:00:00 and on the first line, and then re-save the file to a new location.
That is, [LINETYPE]S[STARTTIME]00:00:00 must always be present, but the other lines that start with [LINETYPE]S need to be removed.
This is what I came up with, which works except it removes all [LINETYPE]S lines, including the first. I can't seem to figure out how to do that part after Googling for a while, so I'm hoping someone can point me in the right direction. Thanks for your help!
Get-ChildItem "C:\Users\Me\Desktop\Samples" -Filter *.txt | ForEach-Object {
Get-Content $_.FullName | Where-Object {
$_ -notmatch "\[LINETYPE\]S"
} | Set-Content ('C:\Users\Me\Desktop\Samples\Final\' + $_.BaseName + '.txt')
}
i couldn't figure out how to do this via a pipeline [blush], so i went with a foreach loop and a compound test.
# fake reading in a text file
# in real life, use Get-Content
$InStuff = #'
[LINETYPE]S[STARTTIME]00:00:00
[LINETYPE]P[STARTTIME]00:00:00
[LINETYPE]B[STARTTIME]00:59:00
[LINETYPE]C[STARTTIME]00:59:00
[LINETYPE]C[STARTTIME]00:59:30
[LINETYPE]S[STARTTIME]01:00:00
[LINETYPE]P[STARTTIME]01:00:00
[LINETYPE]B[STARTTIME]01:59:00
[LINETYPE]C[STARTTIME]01:59:00
[LINETYPE]C[STARTTIME]01:59:30
[LINETYPE]S[STARTTIME]02:00:00
'# -split [System.Environment]::NewLine
$KeepFirst = '[LINETYPE]S'
$FoundFirst = $False
$FilteredList = foreach ($IS_Item in $InStuff)
{
if ($IS_Item.StartsWith($KeepFirst))
{
if (-not $FoundFirst)
{
$IS_Item
$FoundFirst = $True
}
}
else
{
$IS_Item
}
}
$FilteredList
output ...
[LINETYPE]S[STARTTIME]00:00:00
[LINETYPE]P[STARTTIME]00:00:00
[LINETYPE]B[STARTTIME]00:59:00
[LINETYPE]C[STARTTIME]00:59:00
[LINETYPE]C[STARTTIME]00:59:30
[LINETYPE]P[STARTTIME]01:00:00
[LINETYPE]B[STARTTIME]01:59:00
[LINETYPE]C[STARTTIME]01:59:00
[LINETYPE]C[STARTTIME]01:59:30
at that point, you can send the new collection out to a file. [grin]
Try the following:
Get-ChildItem "C:\Users\Me\Desktop\Samples" -Filter *.txt |
Foreach-Object {
$count = 0
Get-Content $_.FullName |
Where-Object { $_ -notmatch '\[LINETYPE\]S' -or $count++ -eq 0 } |
Set-Content ('C:\Users\Me\Desktop\Samples\Final\' + $_.BaseName + '.txt')
}
The script block passed to Where-Object runs in the same scope as the caller, so variable $count can be directly updated.
The 1st line that does contain [LINETYPE]S is included, because $count is 0 at that point, after which $count is incremented ($count++); subsequent [LINETYPE]S are not included, because $count is then already greater than 0.
all.
I'm stuck. I have a PowerShell script which looks to a specific folder for files which are older than 30 days from the last modified date (additionally, it'll create the folder if it doesn't exist). It creates the folder, it gives me the total files, it'll list all of the files in a test query, but it won't actually count the number of 30+ day old files. I've tried several methods to get this count (some deriving from other solutions to delete old files from this site), but PowerShell just doesn't want to do it.
Here's my code so far...
$HomePath = $env:USERPROFILE
$CompanyFolder = "\Company"
$TimeSensativeFolder = "\TimeSensative"
$TimeSensativePath = $HomePath+$CompanyFolder+$TimeSensativeFolder
$OldFilesAmount = 0
$TotalFilesAmount = 0
$TimeLimit = (Get-Date).AddDays(-30)
$StatusOK = "No old files were found in the time sensative folder."
$StatusCreated = "The time sensative folder was created."
$StatusError1 = "There were old files found in the time sensative folder!"
$StatusError2 = "Unable to create the time sensative folder!"
function MakeTimeSensativeFolder ($TimeSensativePath) {
try {
md $TimeSensativePath -Force -ErrorAction Stop
Write-Host $StatusCreated
}
catch {
Write-Host $StatusError2
}
}
function CountOldFiles () {
$OldFilesAmount = $OldFilesAmount + 1
}
if(!(Test-Path $TimeSensativePath -PathType Container)) {
MakePHIFolder $TimeSensativePath
}
else {
}
try {
$TotalFilesAmount = (Get-ChildItem $PHIPath -Recurse -File | Measure-Object).Count
# I've tried this...
Get-Item $PHIPath | Foreach {$_.LastWriteTime} -ErrorAction Stop
if (Get-Content $_.LastWriteTime | Where-Object {$_ -gt $TimeLimit}) {
CountOldFiles
}
# And I've tried this...
Get-ChildItem -Path $PHIPath -Recurse -File | Foreach-Object {
if (Get-Content $_.LastWriteTime | Where-Object {$_ -gt $TimeLimit}) {
CountOldFiles
}
}
# I've even tried this...
Get-ChildItem $PHIPath -Recurse -File | ? {
-not $_.PSIsContainer -and $_.LastWriteTime -lt $TimeLimit
} | CountOldFiles
# And this, as well...
Get-ChildItem -Path $PHIPath -Recurse -File | Where-Object {$_.LastWriteTime -gt $TimeLimit} | CountOldFiles
}
catch {
MakeTimeSensativeFolder $TimeSensativePath
}
# Used for testing.
<#
Get-ChildItem $TimeSensativePath -Recurse -File
Write-Host "TimeSensative folder exists:" $TimeSensativePathExists
Write-Host "Home TimeSensative path:" $TimeSensativePath
Write-Host "Old files found:" $OldFilesAmount
Write-Host "Total files found:" $TotalFilesAmount
Exit
#>
# Determining proper grammar for status message based on old file count.
if ($OldFilesAmount -eq 1) {
$StatusError1 = "There was "+$OldFilesAmount+" old file of "+$TotalFilesAmount+" total found in the PHI folder!"
}
if ($OldFilesAmount -gt 1) {
$StatusError1 = "There were "+$OldFilesAmount+" old files of "+$TotalFilesAmount+" total found in the PHI folder!"
}
# Give statuses.
if ($OldFilesAmount -gt 0) {
Write-Host $StatusError1
}
else {
Write-Host $StatusOK
}
Depending on which I tried, I would get no result or I'd get something like this:
Get-Content : Cannot find drive. A drive with the name '12/22/2016 17' does not exist.
At C:\Users\nobody\Scripts\PS1\ts_file_age.ps1:54 char:14
+ if (Get-Content $_.LastWriteTime | Where-Object {$_ -gt $Tim ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (12/22/2016 17:String) [Get-Content], DriveNotFoundException
+ FullyQualifiedErrorId : DriveNotFound,Microsoft.PowerShell.Commands.GetContentCommand
In any instance, there's no old file count as I'm endeavoring to demand.
It's been a bit of a head scratcher. Any advice?
Thanks so much in advance!
Filtering files with last write time is easy enough. Like so,
$allFiles = gci
$d = (Get-Date).adddays(-30)
$newFiles = #()
$oldFiles = #()
$allFiles | % { if ($_.lastwritetime -ge $d) { $newFiles +=$_ } else { $oldFiles += $_ } }
What's done here is that first all the files are set in a collection. This isn't mandatory, but one can browse the collection to check that it's been populated properly. This is useful in cases one has complex paths or exclusion filters.
The second step is just to get a DateTime that is used later to divide files into old and new ones. Just like the sample did, so nothing interesting here. Actually, there's one little thing. The date is -30 days, but hours, minutes and seconds are based on current time. So if there's really tight limit, consider using midnight time ([datetime]::Today).AddDays(-30)
The third step is to declare two empty collections for new and old files.
The last step is to iterate through the $allFiles and check the last write time. If it's greater or equal to the cutpoint, add it into $newFiles, othervise $OldFiles.
After the last step, further processing should be simple enough.
This is what I do to get (delete in this case) files older than X days:
$Days = 5
$limit = (Get-Date).AddDays(-$Days)
$CurrentDate = Get-Date
#This will delete all files older than 5 days
Get-ChildItem -Path $Workdir -Recurse -Force | Where-Object { !$_.PSIsContainer -and $_.LastWriteTime -lt $limit } | Remove-Item -Force
I have a script set to enter a for each loop every-time a file is created. Once in the loop it will move a file to a another folder and if the same file has to be moved 3 times it will move the file to a different table and remove the record of it from the hash table.
My issue is when I run the script it does not do anything that I write inside the for each loop. Only if I write script above it. Can someone please advise?
$folder = 'C:\Users\jnwankwo\Documents\IUR Test\r' # Enter the root path you want to monitor.
$Failedfolder = 'C:\Users\jnwankwo\Documents\IUR Test\r'
$filter = '*.*' # You can enter a wildcard filter here.
$Files = #{}
$Counter = 1
$folder = 'C:\Users\jnwankwo\Documents\IUR Test\r' # Enter the root path you want to monitor.
$Failedfolder = 'C:\Users\jnwankwo\Documents\IUR Test\r'
$filter = '*.*' # You can enter a wildcard filter here.
$Files = #{}
$Counter = 1
# In the following line, you can change 'IncludeSubdirectories to $true if required.
$fsw = New-Object IO.FileSystemWatcher $folder, $filter -Property #{IncludeSubdirectories = $false;NotifyFilter = [IO.NotifyFilters]'FileName, LastWrite'}
Register-ObjectEvent $fsw Created -SourceIdentifier FileCreated -Action {
ForEach ($file in $folder)
{
$fName = $file.Name
if (-not $Files.ContainsKey($fName))
{
$Files.Add($fName,$Counter)
}
if (($Files.ContainsKey($fName)) -and ($Files.Item($fName) -lt 3))
{
Move-Item 'C:\Users\jnwankwo\Documents\IUR Test\r\*.txt' 'C:\Users\jnwankwo\Documents\IUR Test' -force
$Files.Set_Item($fName,$Counter++)
}
ElseIf (($Files.ContainsKey($fName)) -and ($Files.Item($fName) -eq 3))
{
$Files.clear()
Move-Item 'C:\Users\jnwankwo\Documents\Failed\' $Failedfolder -force
}
}
}
# To stop the monitoring, run the following commands:
# Unregister-Event FileCreated
I have found one thing in your code.
Change ForEach ($file in $folder) to ForEach ($file in (gci $folder))
Here you go, you will have to change the folders back though :)
$folder = 'C:\temp\test' # Enter the root path you want to monitor.
$filter = '*.*' # You can enter a wildcard filter here.
# In the following line, you can change 'IncludeSubdirectories to $true if required.
$fsw = New-Object IO.FileSystemWatcher $folder, $filter -Property #{IncludeSubdirectories = $false;NotifyFilter = [IO.NotifyFilters]'FileName,LastWrite'}
Register-ObjectEvent $fsw Created -SourceIdentifier FileCreated -Action {
$folder = 'C:\temp\test' # Enter the root path you want to monitor.
$Failedfolder = 'C:\temp\test3'
$Files = #{}
$Counter = 1
ForEach ($file in (gci $folder))
{
$fName = $file.Name
if (-not $Files.ContainsKey($fName))
{
$Files.Add($fName,$Counter)
}
if (($Files.ContainsKey($fName)) -and ($Files.Item($fName) -lt 3))
{
Move-Item $file.Fullname 'C:\Users\jnwankwo\Documents\IUR Test' -force
$Files.Item($fName) = $Counter++
}
ElseIf (($Files.ContainsKey($fName)) -and ($Files.Item($fName) -eq 3))
{
$Files.clear()
Move-Item $file.Fullname $Failedfolder -force
}
}
}
Addition:
To store your Hashtable to a file and re-import it on the next run you can use the following code:
#store hashtable to file
$Files.GetEnumerator() | % { "$($_.Name)=$($_.Value)" } | Out-File files_ht.txt
#to import again
$Files = Get-Content files_ht.txt | Convertfrom-StringData
This should enable you to have the data from the hashtable persistent
What I tried to do is searching a directory to output file path, name, version and last modified time into a txt file.
My code as below:
function Get-Version($filePath)
{
$name = #{Name="Name";Expression= {split-path -leaf $_.FileName}}
$path = #{Name="Path";Expression= {split-path $_.FileName}}
$time = #{Name="Last Modified"; Expression={Get-Date $_.LastWriteTime}}
dir -recurse -path $filePath | % { if ($_.Name -match "(.*exe)$") {$_.VersionInfo} } | select $path, $name,$time, FileVersion
}
Get-Version('E:\PS test') >> "version_info.txt"
However the output txt has name, path and version, but no last modified time.
Any hints?
Thanks!
It's because you're returning the .VersionInfo property from your ForEach-Object (%) call, and .LastWriteTime is a property of the file object, not the version info. Have a look at this:
function Get-Version($filePath)
{
$name = #{Name="Name";Expression= {split-path -leaf $_.VersionInfo.FileName}}
$path = #{Name="Path";Expression= {split-path $_.VersionInfo.FileName}}
$time = #{Name="Last Modified"; Expression={Get-Date $_.LastWriteTime}}
$version = #{Name="FileVersion"; Expression={$_.VersionInfo.FileVersion}}
dir -recurse -path $filePath | ? { $_.Name -match "(.*exe)$" } | select $path, $name,$time, $version
}
By Changing the defintiion of $name and $path to refer directly to the version info, you can operate on the original object. I also had $version to get at the FileVersion you were referring to in the select.
That makes the ForEach-Object redundant, since you'd only be passing along the input. Since you were only checking a condition in it anyway, easier to convert it to Where-Object (?).
Expanding your aliases makes it look like this:
function Get-Version($filePath)
{
$name = #{Name="Name";Expression= {Split-Path -Leaf $_.VersionInfo.FileName}}
$path = #{Name="Path";Expression= {Split-Path $_.VersionInfo.FileName}}
$time = #{Name="Last Modified"; Expression={Get-Date $_.LastWriteTime}}
$version = #{Name="FileVersion"; Expression={$_.VersionInfo.FileVersion}}
Get-ChildItem -Recurse -Path $filePath | Where-Object { $_.Name -match "(.*exe)$" } | Select-Object $path, $name,$time, $version
}
However I should also point out that you can filter the file names directly in dir (Get-ChildItem), making the Where-Object superfluous too:
function Get-Version($filePath)
{
$name = #{Name="Name";Expression= {Split-Path -Leaf $_.VersionInfo.FileName}}
$path = #{Name="Path";Expression= {Split-Path $_.VersionInfo.FileName}}
$time = #{Name="Last Modified"; Expression={Get-Date $_.LastWriteTime}}
$version = #{Name="FileVersion"; Expression={$_.VersionInfo.FileVersion}}
Get-ChildItem -Recurse -Path $filePath -Filter *.exe | Select-Object $path, $name,$time, $version
}
And then based on your comment, I realized it can be simplified even more:
function Get-Version($filePath)
{
$path = #{Name="Path";Expression= {$_.DirectoryName}}
$time = #{Name="Last Modified"; Expression={$_.LastWriteTime}}
$version = #{Name="FileVersion"; Expression={$_.VersionInfo.FileVersion}}
Get-ChildItem -Recurse -Path $filePath -Filter *.exe | Select-Object $path, Name,$time, $version
}
$name is not needed because the file object already has a property called .Name that has the file name.
$path can be simplified because $_.DirectoryName already has the path.
$time can be simplified because the .LastWriteTime property is already a [DateTime] so you don't need Get-Date.
The only reason you still need the name/expression hashes for those is to have the fields be named something other than the underlying property. If you don't care about that, you could do this:
function Get-Version($filePath)
{
$version = #{Name="FileVersion"; Expression={$_.VersionInfo.FileVersion}}
Get-ChildItem -Recurse -Path $filePath -Filter *.exe | Select-Object DirectoryName, Name, LastWriteTime, $version
}