Powershell copy files with creation date equals given date - powershell

I'm trying to copy files with creation date equals to given date with no luck
args passed is current date 2/12/2020 and created a few files today but it copy nothing
what am I missing?
CODE
function findLogs ($searchDate)
{
Get-ChildItem c:\logs | Where-Object{ $_.CreationTime -eq $searchDate -and $_.Name -like "logs*" } | ForEach-Object {
$fileName = $_.Name
Copy-Item "c:\$fileName" -Destination c:\backup
}
}

you need truncate time
function findLogs ($searchDate)
{
Get-ChildItem c:\logs | Where-Object{ ([DateTime]::Parse($_.CreationTime.ToString("yyyy-MM-dd") -eq $searchDate -and $_.Name -like "logs*" } | ForEach-Object {
$fileName = $_.Name
Copy-Item "c:\$fileName" -Destination c:\backup
}
}

When working with PowerShell, there are two things to remember.
PowerShell is object-oriented.
PowerShell uses pipelines.
To the extent you can stick to these rules, you will have more options and your work will be easier.
You've done a good job with the pipeline.
Here are some tools to investigate the object side of things.
What objects are being compared?
Here I'll use the date format for my locale. I see you're using day-month-year.
You can use Get-Member to investigate objects in PowerShell.
> "12/2/2020" | Get-Member
TypeName: System.String
Name MemberType Definition
---- ---------- ----------
Clone Method System.Object Clone(), System.Objec…
CompareTo Method int CompareTo(System.Object value),…
Contains Method bool Contains(string value), bool C…
...
Here we see that our date string is a System.String.
I'd like to know what type of object CreationTime is.
> Get-ChildItem -File | Select-Object -First 1 | ForEach-Object CreationTime | Get-Member
TypeName: System.DateTime
Name MemberType Definition
---- ---------- ----------
Add Method datetime Add(timespan value)
AddDays Method datetime AddDays(double value)
AddHours Method datetime AddHours(double value)
...
Here we see that CreationTime is a System.DateTime.
Let's get our values to be the same type.
If you want to do shell-style string comparisons, there is nothing stopping you. But then you would be missing all the richness of PowerShell objects.
In principle, we're comparing two dates. So, let's convert our string input to a DateTime object.
> [DateTime] "12/2/2020"
Wednesday, December 2, 2020 12:00:00 AM
Notice that when we type cast a date string like "12/2/2020" to a DateTime object, a time is automatically filled in for the Hour, Minute, and Second properties. By default, midnight is used.
Solving the problem with the date comparison.
Let's look at one of the DateTime objects from my earlier query.
> Get-ChildItem -File | Select-Object -First 1 | ForEach-Object CreationTime
Saturday, September 26, 2020 10:57:33 AM
In my particular case, my file shows the date Saturday, September 26, 2020 10:57:33 AM.
DateTime objects always include the time. In our case, we don't care about the time. So, one way to solve our problem is to set the time portion of our objects to the same value. Then, when we compare two objects, only the date portion can affect the outcome.
Let's look at the properties and methods of DateTime to see if there is anything that can help us.
The first property on the list is Date. That seems like it might be interesting. Let's look at that.
The description of Date is:
Gets the date component of this instance.
A new object with the same date as this instance, and the time value set to 12:00:00 midnight (00:00:00).
This sounds like it might be useful.
Remember, when we type cast our string date, we got a DateTime object with the time portion set to midnight.
Here, we're taking a date and returning the same date with the time set to midnight.
Now we have a method of setting both our string input and file creation times to the same value. So, when we compare those DateTime objects, we'll just be comparing the date portions. This will solve our problem.
Let's demonstrate the necessary code.
> [DateTime] "12/2/2020"
Wednesday, December 2, 2020 12:00:00 AM
> Get-ChildItem -File | Select-Object -First 1 | ForEach-Object { $_.CreationTime.Date }
Saturday, September 26, 2020 12:00:00 AM
Final Answer
So, let's use [DateTime] type casting and the Date property to fix our function.
Here, I've also added parameters with default values. We can call findLogs in the same way as before. But we also have the option of changing the way the function works without having to rewrite it.
I've also used the FullName property to simplify the file copy.
function findLogs ([string] $searchDate, [string] $source = "c:\logs", [string] $destination = "c:\backup", [string] $nameLike = "logs*" )
{
Get-ChildItem -Path $source -File |
Where-Object { ($_.CreationTime.Date -eq (([DateTime] $searchDate).Date)) -and ($_.Name -like $nameLike) } |
ForEach-Object { Copy-Item -Path $_.FullName -Destination $destination }
}
In my environment, I was able to copy one of my bookmarklets to a backup folder:
$a = "C:\Users\Michael\Desktop\Bookmarklets"
$b = "C:\Users\Michael\Desktop\Backup"
findLogs -searchDate "12/3/2020" -source $a -destination $b -nameLike "Mark*"

Related

Powershell - Formatting column as date

I am importing a CSV which has 5 "columns". One of them is a date and time. The 3rd party software that is reading this column, then does not sort the date well.
IE: (4/8/2022 1:24:08 PM) will sort above (4/13/2022 8:51:52 AM)
Even though 4/13 is after 4/8 it will not sort it properly. I would like to add a leading zero in front of the month and date with powershell. I did do some searching but nothing seems to make sense to me, I am not a HUGE programmer.
Thanks for any help!
This is what I am currently doing. I am using unique to remove duplicate rows (this is needed for what I am doing).
$FinalSessions = Import-Csv -Path "C:\Windows\AdminArsenal\UserSessions.csv" | Sort-Object * -Unique
$FinalSessions | Export-Csv -Path "C:\Windows\AdminArsenal\UserSessions.csv" -NoTypeInformation
$FinalSessions
You can use Get-Date to actually get a datetime object and then reformat it.
It would look something like this:
$FinalSessions = Import-Csv -Path "C:\Windows\AdminArsenal\UserSessions.csv"| Sort-Object * -Unique
$FinalSessions | % { $_.DateColumn = Get-Date $_.DateColumn -Format "MM/dd/yyyy hh:mm:ss tt" }
$FinalSessions | Export-Csv -Path
"C:\Windows\AdminArsenal\UserSessions.csv" -NoTypeInformation
$FinalSessions
Just replace "DateColumn" with the name of your column
Assuming that the column that contains the date-time string is named Date (adjust as needed):
Import-Csv -Path C:\Windows\AdminArsenal\UserSessions.csv |
ForEach-Object { $_.Date = ([datetime] $_.Date).ToString('s') } |
Sort-Object * -Unique -OutVariable finalSessions |
Export-Csv -Path C:\Windows\AdminArsenal\UserSessions.csv -NoTypeInformation
$finalSessions
Note that the s format specifier (in ISO 8601 format) is used to reformat the date-time strings, as that results in a string whose lexical sorting reliably indicates chronological order, across year boundaries; e.g. 2022-05-05T17:52:47

How Do I sort files based on both date time in Powershell ? [sort files with same time but different dates]

I have multiple files in a directory created at the same time but on different dates.
$files = Get-ChildItem C:\File*.txt | Sort-Object { $_.CreationTime } | Select-Object Name
$files[0] doesn't seem to be picking the oldest file(File1) that was created. Which property of Sort-Object can I use to sort files based on both date and time?
Example :
File1_20220107 123001 AM.txt
File2_20220109 123001 AM.txt
File3_20220110 123001 AM.txt
Building on Santiago Squarzon's helpful comments:
Sorting by the .CreationTime property of the [System.IO.FileInfo] instances that Get-ChildItem outputs works fine and relies on the file-system metadata for the timestamp of each file's creation.
A string representation of a (creation) date embedded in a file's name may or may not reflect the file's actual creation timestamp (and even if it did, it wouldn't be an exact match, given that the file-system-maintained timestamps have sub-second granularity.
To sort by actual .CreationTime, as reported by the file-system, you can use the following simplified version of your own attempt, given that there's no need for a script block ({ ... }) to reference the .CreationTime property:
Get-ChildItem C:\File*.txt | Sort-Object CreationTime | Select-Object Name
To sort by the timestamp string embedded in your file names:
Get-ChildItem C:\File*.txt |
Sort-Object {
[datetime] (
$_.BaseName -replace '^.+_(\d{4})(\d{2})(\d{2}) (\d{2})(\d{2})', '$1-$2-$3 $4:$5:'
)
} |
Select-Object Name

Getting unique elements from a column returns one value

I have the following function defined in a ps1 file, using the DLL from the latest taglib release. I downloaded the nuget package and ran expand-archive on it and copied the DLL to the correct place.
[System.Reflection.Assembly]::LoadFrom((Resolve-Path ($PSScriptRoot + "\TagLibSharp.dll")))
function Get-Image {
[CmdletBinding()]
param (
[Parameter(ValueFromPipelineByPropertyName)] $Name
)
process {
$fullPath = (Resolve-Path $Name).Path
$image = [taglib.file]::create($fullpath)
return $image
}
}
function Get-ImmediatePhotos {
Get-ChildItem | Where-Object {$_.Extension -eq ".jpg" -or $_.Extension -eq ".png"} | Get-Image
}
When I run this command to extract the years from the EXIF data in photos in a given directory I get a table like this:
> $years = Get-ImmediatePhotos | select-object {$_.Tag.DateTime.Year}
> $years
$_.Tag.DateTime.Year
--------------------
2020
2020
2020
2020
2020
2020
2020
2020
2020
2020
2021
2021
2021
2021
If I then try to extract unique years with sort-object I only get one year!
> $years | sort-object -unique
$_.Tag.DateTime.Year
--------------------
2020
If I try to group years with group-object I get this error:
$years | group-object
Group-Object: Cannot compare "#{$_.Tag.DateTime.Year=2020}" to
"#{$_.Tag.DateTime.Year=2020}" because the objects are not the same type or the object
"#{$_.Tag.DateTime.Year=2020}" does not implement "IComparable".
This seems to be telling me that the type of values in the column are some sort of anonymous thing which can't be compared.
How do I use the results of select as values, such as strings? My end goal is to automatically sort and categorize photos into directories in /year/month format.
Give your calculated property a proper name so that you can reference it later when using Sort-Object or Group-Object:
$years = Get-ImmediatePhotos | select-object #{Name='YearTaken';Expression={$_.Tag.DateTime.Year}}
$years |Sort-Object YearTaken -Unique
# or
$years |Group-Object YearTaken
Altenatively, use ForEach-Object instead of Select-Object - ForEach-Object will spit out the raw Year values (as opposed to an object having the value stored in a nested property, which is what Select-Object gives you):
$years = Get-ImmediatePhotos | ForEach-Object {$_.Tag.DateTime.Year}
# `$years` is just an array of raw Year values now, no need to specify a key property
$years |Sort-Object -Unique
# or
$years |Group-Object
To complement Mathias R. Jessen's helpful answer with why what you tried didn't work:
The immediate problem is that Sort-Object-Unique considers all [pscustomobject] instances to be equal, even if they have different property values, and even if they have different sets of properties.
Note that your Select-Object call, due to use of the (positionally implied) -Property parameter, does not extract just the years from its input objects, but creates a [pscustomobject] wrapper object with a property containing the year.
Normally, -ExpandProperty is used to extract just the values of a single property, but with a calculated property, such as in your case, this doesn't work. However, passing the exact same script block to ForEach-Object does work.
Therefore, to make your original code work, you need to pass the name of the properties whose values should be sorted and used as the basis for the uniqueness determination:
$years | sort-object -unique -property '$_.Tag.DateTime.Year'
Note the strange property name, which results from your Select-Object call having used just a script block ({ ... }) as a calculated property, in which case the script block's literal source code becomes the property name (the result of calling .ToString() on the script block).
Typically, a hashtable is used to define a calculated property, which allows naming the property, as shown in Mathias' answer.
As an aside: If you want to merely sort by year while passing the original image objects through, you can use your script block as-is directly with Sort-Object's (positionally implied) -Property parameter:
# Sort by year, but output the image objects.
Get-ImmediatePhotos | Sort-Object {$_.Tag.DateTime.Year}
Note that in this use of a calculated property, direct use of a script block is appropriate and usually sufficient, as the property is purely used for sorting, and doesn't require a name.
However, Sort-Object too supports hashtable-based calculated properties, but such hashtables do not support a name / label entry (because it would be meaningless), but you can use either ascending or descending as a Boolean entry to control the sort order on a per-property basis.
That is, the following is the verbose equivalent of the command above:
Get-ImmediatePhotos | Sort-Object #{ ascending=$true; expression={$_.Tag.DateTime.Year} }

Setup default date format like yyyy-mm-dd in Powershell?

A simple & short question:
How can I setup a default date format in powershell like yyyy-mm-dd ? so any date output will be like this format?
or How to setup a date format globally in one script ?
Is there a way to output date only without time? when I output LastWriteTime, Default is
13-03-2014 14:51
I only need 13-03-2014 but 14:51.
A date in PowerShell is a DateTime object. If you want a date string in a particular format, you can use the built-in string formatting.
PS C:\> $date = Get-Date
PS C:\> $date.ToString("yyyy-MM-dd")
2014-04-02
You can also use the string format (-f) operator:
PS C:\> "{0:yyyy-MM-dd}" -f $date
2014-04-02
The LastWriteTime property of a file is a DateTime object also, and you can use string formatting to output a string representation of the date any way you want.
You want to do this:
Get-ChildItem -Recurse \\path\ -filter *.pdf | Select-Object LastWriteTime,Directory
You can use a calculated property:
Get-ChildItem C:\Users\Administrator\Documents -filter *.pdf -Recurse |
Select-Object Directory, Name, #{Name="LastWriteTime";
Expression={$_.LastWriteTime.ToString("yyyy-MM-dd HH:mm")}}
Run
help Select-Object -Full
and read about calculated properties for more information.
i've used this, it works for me, just copy it at the beginning of your script
$currentThread = [System.Threading.Thread]::CurrentThread
$culture = [CultureInfo]::InvariantCulture.Clone()
$culture.DateTimeFormat.ShortDatePattern = 'yyyy-MM-dd'
$currentThread.CurrentCulture = $culture
$currentThread.CurrentUICulture = $culture
in case you'll find problem in loading assembly for CultureInfo (i had this issue on Windows 2008 Server), change line 2 in this way
$currentThread = [System.Threading.Thread]::CurrentThread
$culture = $CurrentThread.CurrentCulture.Clone()
$culture.DateTimeFormat.ShortDatePattern = 'dd-MM-yyyy'
$currentThread.CurrentCulture = $culture
$currentThread.CurrentUICulture = $culture
for always usage you can add in your .\Documents\WindowsPowerShell\profile.ps1
$culture = (Get-Culture).Clone()
$culture.DateTimeFormat.ShortDatePattern = 'yyyy-MM-dd'
Set-Culture $culture

LastAccessTime and get-date Comparison

I am parsing through a directory with multiple sub-directories and want to compare the LastAccessed time with the get-date time to see if the file has been accessed since yesterday, and based on that I will either delete the file or leave it alone. I have tried piping the get-date results out to a text file and pull it back as a string, I have tried wildcard I have even gone as far as using the -like as opposed to -eq in order to get the comparison to work, but it is not properly comparing the data. Any help would be greatly appreciated.
Here is my current code:
$servers="servera","serverb"
$date3=get-date -Format d
foreach($a in $servers){
$CTXGPDir="\C$\ProgramData\Citrix\GroupPolicy"
$CTXGPDirFP="\\"+"$a"+"$CTXGPDir"
$CTXGPUserDirstoRM=Get-ChildItem "$CTXGPDirFP"|where-Object{$_.Name -notlike "*.gpf"}
foreach($i in $CTXGPUserDirstoRM){
$datestring="$date3"+" *"
$CTXUserGPPath="\C$\ProgramData\Citrix\GroupPolicy\$i"
$CTXUserGPFP="\\"+"$a"+"$CTXUserGPPath"
$file=get-item $CTXUserGPFP
$isFileInactive=$file|select-object -expandproperty LastAccessTime
write-host $file
write-host $isFileInactive
write-host $datestring
if($isFileInactive -like "$datestring *"){write-host "$CTXUserGPFP on $a has lastwritetime of $isFileInactive and should NOT BE deleted"}
if($isFileInactive -notlike "$datestring *"){write-host "$CTXUserGPFP on $a has lastwritetime of $isFileInactive and SHOULD BE deleted"}
}
Your date comparison is deeply flawed.
get-date -format d returns a String representing the current date based on your regional settings.
get-childitem <file> | select -expandproperty lastaccesstime returns a DateTime object, which gets formatted as a "long" date/time using your regional settings.
To compare these two dates effectively, you need to convert the latter to the same format.
$isFileInactive=($file|select-object -expandproperty LastAccessTime).ToShortDateString()
$isFileInactive is now a String formatted the same as you get with get-date -format d and you can make a proper comparison.
if($isFileInactive -eq $datestring){write-host "$CTXUserGPFP on $a has lastwritetime of $isFileInactive and should NOT BE deleted"}
If you have to deal with timezones, you may want to amend it to add .ToLocalTime() before ToShortDateString();