Get-ChildItem refresh/update - powershell

Pretty new to Powershell and hoping someone can point me in the right direction. Im trying to figure out if there is a cleaner way to accomplish what I have below? Is there a way to refresh to contents of Get-ChildItem once I have made some changes to the files which are returned during the first Get-ChildItem call (stored in $items variable)?
During the first foreach statement I am creating a log signature for all the files that are returned. Once that is done, what I need to do is; get a listing once again (because the item in the path have changed), the second Get-ChildItem will include both the files that were found during the first Get-ChildItem call and also all the logFiles that were generated when the first foreach statement called the generate-LogFile function. So my question, is there a way to update the listing without having to call get-chilItem twice, as well as use two foreach statements?
Thanks for all the help!
--------------This is what I changed the code based on recommendation--------------
$dataStorePath = "C:\Process"
function print-All($file)
{
Write-Host "PrintALL filename:" $file.FullName #Only prints when print-All($item) is called
}
function generate-LogFile($file)
{
$logName = $file.FullName + ".log"
$logFilehandle = New-Object System.IO.StreamWriter $logName
$logFilehandle.Writeline($logName)
$logFilehandle.Close()
return $logName
}
$items = Get-ChildItem -Path $dataStorePath
foreach ($item in $items)
{
$log = generate-LogFile($item) #Contains full path C:\Process\$fileName.log
print-All($item)
print-All($log) #When this goes to the function, nothing prints when using $file.FullName in the print-All function
}
---------Output--------------
For testing I have two files in C:\Process:
fileA.txt & fileB.txt
I will create two additional files
fileA.txt.log & fileB.txt.log
Eventually I need to do something with all four files. I created a print-All Function where I would process all four files. Below is the current ouput. As can be seen, I only get output for the two original files found, not the two additional created (get blank lines when calling the print-All($log)). I need to able to use fullpath property provided by Get-ChildItem, thus using FullName
PrintALL filename: fileA.txt
PrintALL filename:
PrintALL filename: fileB.txt
PrintALL filename:

I'm not entirely clear on what you are asking, by can have generate-LogFile return the created log file, then just call generateRequestData on both your file and the log file? Something like this:
$items = Get-ChildItem -Path $dataStorePath
foreach ($file in $items)
{
$logFile = generate-LogFile $file
generateRequestData $file
generateRequestData $logFile
}
Edit:
In your added sample, you are returning a string from generate-LogFile. .NET strings don't have a FullName property, so nothing gets printed in print-All. To get the FileInfo object that you want, use the get-item commandlet:
return Get-Item $logName
Also, in this example, you don't need to use a StreamWriter to write to the file, you could use the native powershell Out-File commandlet:
function generate-LogFile($file)
{
$logName = $file.FullName + ".log"
$logName | Out-File $logName
return Get-Item $logName
}

Related

Powershell: ForEach Copy-Item doesn't rename properly when retrieving data from array

I am pretty new to PowerShell and I need some help. I have a .bat file that I want to copy as many times as there are usernames in my array and then also rename at the same time. This is because the code in the .bat file remains the same, but for it to work on the client PC it has to have the username as a prefix in the filename.
This is the code that I have tried:
$usernames = Import-Csv C:\Users\Admin\Desktop\usernames.csv
$file = Get-ChildItem -Path 'C:\Users\Admin\Desktop\generatedbat\' -Recurse
foreach ($username in $usernames)
{
ForEach-Object {Copy-Item $file.FullName ('C:\Users\Admin\Desktop\generatedbat\' + $username + $File.BaseName + ".bat")}
}
This copies everything and it kind of works but I have one problem.
Instead of having this filename: JohnR-VPNNEW_up.bat
I get this: #{Username=JohnR}-VPNNEW_up.bat
Any help? Thanks!
So you have one .bat file C:\Users\Admin\Desktop\generatedbat\VPNNEW_up.bat you want to copy to the same directory with new names taken from the usernames.csv --> Username column.
Then try
# get an array of just the UserNames column in the csv file
$usernames = (Import-Csv -Path 'C:\Users\Admin\Desktop\usernames.csv').Username
# get the file as object so you can use its properties
$originalFile = Get-Item -Path 'C:\Users\Admin\Desktop\generatedbat\VPNNEW_up.bat'
foreach ($username in $usernames) {
$targetFile = Join-Path -Path $originalFile.DirectoryName -ChildPath ('{0}-{1}' -f $username, $originalFile.Name)
$originalFile | Copy-Item -Destination $targetFile -WhatIf
}
I have added switch -WhatIf so you can first test this out. If what is displayed in the console window looks OK, then remove that -WhatIf safety switch and run the code again so the file is actually copied
I kept the code the same but instead of using a .csv file I just used a .txt file and it worked perfectly.

Powershell: Logging foreach changes

I have put together a script inspired from a number of sources. The purpose of the powershell script is to scan a directory for files (.SQL), copy all of it to a new directory (retain the original), and scan each file against a list file (CSV format - containing 2 columns: OldValue,NewValue), and replace any strings that matches. What works: moving, modifying, log creation.
What doesn't work:
Recording in the .log for the changes made by the script.
Sample usage: .\ConvertSQL.ps1 -List .\EVar.csv -Files \SQLFiles\Rel_1
Param (
[String]$List = "*.csv",
[String]$Files = "*.sql"
)
function Get-TimeStamp {
return "[{0:dd/MM/yyyy} {0:HH:mm:ss}]" -f (Get-Date)
}
$CustomFiles = "$Files\CUSTOMISED"
IF (-Not (Test-Path $CustomFiles))
{
MD -Path $CustomFiles
}
Copy-Item "$Files\*.sql" -Recurse -Destination "$CustomFiles"
$ReplacementList = Import-Csv $List;
Get-ChildItem $CustomFiles |
ForEach-Object {
$LogFile = "$CustomFiles\$_.$(Get-Date -Format dd_MM_yyyy).log"
Write-Output "$_ has been modified on $(Get-TimeStamp)." | Out-File "$LogFile"
$Content = Get-Content -Path $_.FullName;
foreach ($ReplacementItem in $ReplacementList)
{
$Content = $Content.Replace($ReplacementItem.OldValue, $ReplacementItem.NewValue)
}
Set-Content -Path $_.FullName -Value $Content
}
Thank you very much.
Edit: I've cleaned up a bit and removed my test logging files.
Here's the snippet of code that I've been testing with little success. I put the following right under $Content= Content.Replace($ReplacementItem.OldValue, $ReplacementItem.NewValue)
if ( $_.FullName -like '*TEST*' ) {
"This is a test." | Add-Content $LogFile
}
I've also tried to pipe out the Set-Content using Out-File. The outputs I end up with are either a full copy of the contents of my CSV file or the SQL file itself. I'll continue reading up on different methods. I simply want to, out of hundreds to a thousand or so lines, to be able to identify what variables in the SQL has been changed.
Instead of piping output to Add-Content, pipe the log output to: Out-File -Append
Edit: compare the content using the Compare-Object cmdlet and evaluate it's ouput to identify where the content in each string object differs.

Compress File per file, same name

I hope you are all safe in this time of COVID-19.
I'm trying to generate a script that goes to the directory and compresses each file to .zip with the same name as the file, for example:
sample.txt -> sample.zip
sample2.txt -> sample2.zip
but I'm having difficulties, I'm not that used to powershell, I'm learning and improving this script. In the end it will be a script that deletes files older than X days, compresses files and makes them upload in ftp .. the part of excluding with more than X I've already managed it for days, now I grabbed a little bit on this one.
Last try at moment.
param
(
#Future accept input
[string] $InputFolder,
[string] $OutputFolder
)
#test folder
$InputFolder= "C:\Temp\teste"
$OutputFolder="C:\Temp\teste"
$Name2 = Get-ChildItem $InputFolder -Filter '*.csv'| select Name
Set-Variable SET_SIZE -option Constant -value 1
$i = 0
$zipSet = 0
Get-ChildItem $InputFolder | ForEach-Object {
$zipSetName = ($Name2[1]) + ".zip "
Compress-Archive -Path $_.FullName -DestinationPath "$OutputFolder\$zipSetName"
$i++;
$Name2++
if ($i -eq $SET_SIZE) {
$i = 0;
$zipSet++;
}
}
You can simplify things a bit, and it looks like most of the issues are because in your script example $Name2 will contain a different set of items than the Get-ChildItem $InputFolder will return in the loop (i.e. may have other objects other than .csv files).
The best way to deal with things is to use variables with the full file object (i.e. you don't need to use |select name). So I get all the CSV file objects right away and store in the variable $CsvFiles.
We can additionally use the special variable $_ inside the ForEach-Object which represents the current object. We also can use $_.BaseName to give us the name without the extension (assuming that's what you want, otherwise use $_Name to get a zip with the name like xyz.csv).
So a simplified version of the code can be:
$InputFolder= "C:\Temp\teste"
$OutputFolder="C:\Temp\teste"
#Get files to process
$CsvFiles = Get-ChildItem $InputFolder -Filter '*.csv'
#loop through all files to zip
$CsvFiles | ForEach-Object {
$zipSetName = $_.BaseName + ".zip"
Compress-Archive -Path $_.FullName -DestinationPath "$OutputFolder\$zipSetName"
}

using a delimiter to split files names in powershell

I use a simple function to download files and return the path to me when updating computers for simplicity.
I was stuck on why it was not working then realized that the proxy is appending a random number to the filename so instead of it being 12345.zip it is actually 8493830_12345.zip.
I have tried to find the file using the "_" as a split but while there are no errors, the file is not being returned and I have checked it is there manually.
function FileCheck {
$fileName.Split("_")[1]
$fileName = "{0}.zip" -f 12345
Download -ZipFileName $($fileName) -OutputDirectory $env:temp
$SleepTime = 300
$sleepElapsed = 0
$sleepInterval = 20
Start-Sleep $sleepInterval
$file = Get-ChildItem -Path $env:temp -Filter "$fileName*"
if ($file -ne $null) {
return $file[0].FullName
}
Start-Sleep($sleepInterval)
$sleepElapsed += $sleepInterval
if (($SleepTime) -le $sleepElapsed){
# Check for file with given prefix
$file = Get-ChildItem -Path $env:temp -Filter "$fileName*"
if ($file -eq $null) {
Write-Error 'file not found'
return $null
}
return $file[0].FullName
}
}
I am guessing the split is not working but googling and moving the filename.split has not worked for me. Any help is appreciated
Well, your split is doing nothing at all. You haven't defined $filename, but if you had, and it had an underscore, then $filename.split('_') would return two or more strings, depending on how many underscores were in the original string, but you never capture the result. I think the real problem here is the filter you are applying to Get-ChildItem later in your function.
$file = Get-ChildItem -Path $env:temp -Filter "$fileName*"
That will look for files beginning with $fileName, which you define on line 4 to be "12345.zip". That is exactly the opposite of what you want to be looking for. You need to move the asterisk to before $fileName, so it looks like this:
$file = Get-ChildItem -Path $env:temp -Filter "*$fileName"
That will return all files that end with "12345.zip", which would include things like:
myfuzzyhippo12345.zip
learn-to-count-12345.zip
8493830_12345.zip
Basically anything that ends in 12345.zip. Also, it appears that you are under the impression that executing a return $file[0].fullname or return $null will stop the function. That's a mistake. A function runs to completion unless exited early by something like a break command. Also, everything not explicitly captured or redirected will be passed back from the function, so reading through your function people are likely to get the output of your $filename.split('_') line, then possibly $null or $filename[0].fullname.
Lastly, it appears that you're trying to look for the file, if you don't find it to wait a bit, and try again, until $sleepElapsed is greater than $sleepTime. What you want here is a While or a Do/While loop. Here's what I'd do...
function FileCheck {
Param(
$fileName = '12345.zip',
$SleepTime = 300,
$sleepElapsed = 0,
$sleepInterval = 20
)
Download -ZipFileName $($fileName) -OutputDirectory $env:temp
Do{
Start-Sleep $sleepInterval
$sleepElapsed = $sleepElapsed + $sleepInterval
$file = Get-ChildItem -Path $env:temp -Filter "*$fileName"|Select -First 1
}While(!$file -and $sleepElapsed -le $sleepTime)
$file.FullName
}
That lets you define things like sleep settings at runtime if you want, or just let it default to what you were using, same with the file name. Then it downloads the file, and looks for it, pausing between attempts, until either it finds the file, or it runs out of time. Then it returns $file.FullName which is either the path to the file if it found one, or nothing if it didn't find a file.
Personally I'd have it return the file object, and just utilize the .FullName property if that's all I wanted later. Usually (not always, but usually) more info returned from a function is better than less info. Like what if the download fails and it's a zero byte file? Just returning only the path doesn't tell you that.

Parse directory listing and pass to another script?

I am trying to write a PowerShell script that will loop through a directory in C:\ drive and parse the filenames with the file extension to another script to use.
Basically, the output of the directory listing should be accessible to be parsed to another script one by one. The script is a compiling script which expects an argument (parameter) to be parsed to it in order to compile the specific module (filename).
Code:
Clear-Host $Path = "C:\SandBox\"
Get-ChildItem $Path -recurse -force | ForEach { If ($_.extension -eq ".cob")
{
Write-Host $_.fullname
}
}
If ($_.extension -eq ".pco")
{
Write-Host $_.fullname }
}
You don't need to parse the output as text, that's deprecated.
Here's something that might work for you:
# getmyfiles.ps1
Param( [string])$Path = Get-Location )
dir $Path -Recurse -Force | where {
$_.Extension -in #('.cob', '.pco')
}
# this is another script that calls the above
. getmyfile.ps1 -Path c:\sandbox | foreach-object {
# $_ is a file object. I'm just printing its full path but u can do other stuff eith it
Write-host $_.Fullname
}
Clear-Host
$Path = "C:\Sandbox\"
$Items = Get-ChildItem $Path -recurse -Include "*.cob", "*.pco"
From your garbled code am guessing you want to return a list of files that have .cob and .pco file extensions. You could use the above code to gather those.
$File = $Items.name
$FullName = $items.fullname
Write-Host $Items.name
$File
$FullName
Adding the above lines will allow you to display them in various ways. You can pick the one that suites your needs then loop through them on a for-each.
As a rule its not a place for code to be writen for you, but you have tried to add some to the questions so I've taken a look. Sometimes you just want a nudge in the right direction.