For example, here's a folder and file structure:
Code: Select all
root
└── A/
└── B/
└── abcd.txtCode: Select all
root
└── A/
└── B/
└── abcd.txtYes, by design, but not by logic.
Yeah, and it is a very frustrating situation. Because, maybe it follows some computer logic of its own, but from a human logic point of view, as I described above, it's just too weird.void wrote: Sun Aug 18, 2024 11:48 pm The OS doesn't update the date modified timestamp all the way to the root. (grand parents)
Only the direct parent is updated.
Everything reads the date modified timestamp from the OS.
Oh. I already tried this option before writing my post, but didn't get what I wanted, and now I understand why - despite the fact the modified dates of the child files were changed, their sizes remained the same.void wrote: Sun Aug 18, 2024 11:48 pm There is a always_update_folder_recent_change advanced setting.
When enabled, all parents to the root are updated for the Date Recently Changed property..
However, grand parents will only update when a child file size changes.
When I try to sort files and folders in Total Commander or Windows Explorer by Date modified, I want to see the correct sorting order in whch the changes in the modified date of all files and folders would be reflected properly, but not like at the moment, when folder "A", which contains subfolder "B" with a younger modified date than folder "A", is at the end of the sorting list.void wrote: Sun Aug 18, 2024 11:48 pm What are you trying to do with this information?
A script to update the timestamp from child folders (and grand child folders) might be the way to go..
Code: Select all
#Set-PSDebug -Trace 2
# Print the full command line used to run the script
Write-Host "Script was run with the following command:"
Write-Host $MyInvocation.Line
Write-Host ""
# Function to split the input string based on the pattern "space followed by drive letter and a path"
function Split-ArgumentsByPattern {
param (
[string]$inputString
)
# Regular expression pattern to match space followed by drive letter and path
$pattern = '(?<=\s|"|^)([A-Za-z]:\\[^\/\:\*\?\"\<\>\|]+(?= [A-Za-z]:|"|$))'
# Split the string by the pattern
$folders = [regex]::Matches($inputString, $pattern) | ForEach-Object { $_.Value }
# Remove any leading or trailing whitespace from each folder path
$folders = $folders | ForEach-Object { $_.Trim() } | Where-Object { $_ -ne "" }
return $folders
}
# Combine all arguments into a single string
Write-Host "Arguments : $args"
Write-Host ""
#$combinedArgs = $args -join " "
#Write-Host "Combined Arguments: $combinedArgs"
#Write-Host ""
# Split the combined string into individual folder paths
$rootFolders = Split-ArgumentsByPattern -inputString $args #$combinedArgs
# Print each folder path passed in the array
Write-Host "RootFolders from arguments:"
foreach ($folder in $rootFolders) {
Write-Host "- $folder"
}
# Function to update the parent folder's LastWriteTime
function Update-ParentFolderTime {
param (
[string]$folderPath
)
# Get all items (files and subfolders) in the current folder
$items = Get-ChildItem -Path $folderPath -Recurse
# Find the item with the most recent LastWriteTime
$mostRecentItem = $items | Sort-Object LastWriteTime -Descending | Select-Object -First 1
# If there's a most recent item, compare its LastWriteTime with the parent folder
if ($mostRecentItem) {
$parentFolder = Get-Item -Path $folderPath
if ($parentFolder.LastWriteTime -lt $mostRecentItem.LastWriteTime) {
# Update the parent folder's LastWriteTime to the most recent item's LastWriteTime
Write-Host "6. Updating LastWriteTime of the folder '$folderPath' from $($parentFolder.LastWriteTime) to $($mostRecentItem.LastWriteTime)" -ForegroundColor Red
$parentFolder.LastWriteTime = $mostRecentItem.LastWriteTime
} else {
Write-Host "7. The folder '$folderPath' has the most recent LastWriteTime than its child files and subfolders" -ForegroundColor DarkGreen
}
}
}
# Function to recursively process all folders starting from the bottom level
function Process-Folders {
param (
[string]$rootFolder
)
# Get all subfolders
$subfolders = Get-ChildItem -Path $rootFolder -Directory
Write-Host "1. $subfolders = Get-ChildItem -Path $rootFolder -Directory"
Write-Host "2. $subfolders"
foreach ($subfolder in $subfolders) {
# Recursively process subfolders
Write-Host "3. Process-Folders -rootFolder $($subfolder.FullName)"
Process-Folders -rootFolder $subfolder.FullName
}
# Update the currently processed root folder
Write-Host "5. Update-ParentFolderTime -folderPath $rootfolder"
Update-ParentFolderTime -folderPath $rootFolder
}
# Validate that at least one folder is provided as argument
if (-not $rootFolders) {
Write-Host '
--------------------------------------------------------
Please provide one or more root folder paths as arguments.
For example:
.\UpdateFolderModifiedDate.ps1 "C:\path\to\folder"
or
.\UpdateFolderModifiedDate.ps1 C:\path\to\folder
or
.\UpdateFolderModifiedDate.ps1 "C:\path\to\folder" "C:\path\to\another\folder" "C:\path\to\yet\another\folder"
or
.\UpdateFolderModifiedDate.ps1 C:\path\to\folder C:\path\to\another\folder C:\path\to\yet\another\folder
--------------------------------------------------------
'
# If no arguments were provided, prompt the user to enter one or more folder paths
$input = Read-Host 'Or please enter the paths of the folders you want to process (without/with quotes, just separate multiple paths with the space/s).
For example:
"C:\path\to\folder" "C:\path\to\another\folder"
C:\path\to\folder C:\path\to\another\folder
'
# If the user provided folder paths, split them into an array
if ($input) {
$rootFolders = Split-ArgumentsByPattern -inputString $input # -split ",\s*" # Split by comma, allowing for optional spaces
Write-Host "Input: $input"
Write-Host "RootFolders from input: $rootFolders"
} else {
Write-Host "No folder paths provided. Exiting script."
Set-PSDebug -Trace 0
exit 1
}
}
# Loop through each provided folder and process it
foreach ($rootFolder in $rootFolders) {
if (Test-Path $rootFolder) {
Write-Host "RootFolders from arguments: $rootFolders"
Write-Host "Processing folder: $rootFolder"
Process-Folders -rootFolder $rootFolder
} else {
Write-Host "Folder not found: $rootFolder"
}
}
Set-PSDebug -Trace 0Code: Select all
TOTALCMD#BAR#DATA
powershell.exe -noexit -ExecutionPolicy Bypass "X:\path\to\the\script\UpdateFolderModifiedDate.ps1"
'%X%Y%P%S%T%R'
C:\Program Files (x86)\Total Commander\Wcmicons.dll,51
Change the modification date of folders to match the most recent modification date of their child files or subfolders.
-1
Code: Select all
Directory of T:\K-ORSAIR\LIB\PLAYERS\
----------------------------------------------------------------------------
_d____ Oct 24, 2022 21:01:42 [MPlayer Skins]
_d____ Sep 12, 2022 12:13:55 [MPlayer]
_d____ Sep 07, 2022 12:07:26 [MediaPlayerClassic]
_d____ Mar 07, 2022 13:44:09 [VLC Video Lan]
_d____ Oct 27, 2021 14:23:05 [OLD]
_d____ Jul 03, 2013 11:31:19 [WMP Skins]
_d____ May 20, 2013 13:47:06 [KMPlayer Skins]
_d____ Nov 03, 2007 22:05:44 [VLC Skins]
1,527,829 ______ Dec 28, 2006 20:50:00 zp500std (zoom player free).exe
2,986,272 ______ Dec 28, 2006 20:49:00 zp500pro (zoom player pro).exe
933,478 ______ Feb 18, 2004 14:05:00 zoomplayer331std.zip
696,910 ______ Nov 07, 2002 09:55:00 zoomplayer290.zip
I will consider a search to list 'modified' folders where:If I want to know if there are any changes occurred in folder "A" - how can I do that?
I want the changes in the modified date of all descendants (files and subfolders) to be reflected on the modified date of all parent(grandfather, great-grandfather, great-great-grandfather etc.) folders.void wrote: Tue Aug 20, 2024 11:36 pm a descendant folder has a modified date that is more recent than the modified date of the containing folder.
(I suspect this is what you want?)
Yes, they will be marked modified, but with the corresponding date of this modification, which will allow them to be sorted properly by Date Modified.void wrote: Tue Aug 20, 2024 11:36 pm This might be unusable because most folders will be marked 'modified'.
How?void wrote: Tue Aug 20, 2024 11:36 pm Won't folders like C:, C:\Users, C:\Users\My User Name, C:\Users\My User Name\AppData get in the way?
Will it help to properly sort files and folders by the Date Modified column in TC or Windows Explorer?void wrote: Tue Aug 20, 2024 11:36 pm I will consider a property to show the timestamp of the most recently modified descendant.
Because programs will constantly write to %APPDATA%.How?Won't folders like C:, C:\Users, C:\Users\My User Name, C:\Users\My User Name\AppData get in the way?
Everything only.Will it help to properly sort files and folders by the Date Modified column in TC or Windows Explorer?
Is it possible to do this not only via limiting to a specific folder, but more broadly through blacklisting/whitelisting?void wrote: Sun Aug 25, 2024 2:04 am Because programs will constantly write to %APPDATA%.
These folders will always have the current date/time.
You will likely need to limit your search results to a particular folder.
Could it be implemented in such a way that allows proper sorting by Date modified not only in Everything, but also in the system as a whole?void wrote: Sun Aug 25, 2024 2:04 amEverything only.Will it help to properly sort files and folders by the Date Modified column in TC or Windows Explorer?
But I don't see any date modifications of the root folder if I make changes to files at depth 2 and below.When enabled, any change made to the Everything folder database entry will cause the recent change date to update.
This includes folder size changes.
This setting will update the date recently changed property for folders for every change single event. (even when there is no actual change)
This includes folder size changes.
For your use case, I recommend writing a script that traverses the desired folders and sets the filetime of all parents.Is it possible to do this not only via limiting to a specific folder, but more broadly through blacklisting/whitelisting?
Code: Select all
#include <stdlib.h>
#include <stdio.h>
#include <windows.h>
// update parent to latest file timestamp:
void parse_file2folder_date(char *dstpath)
{
char dstbuf[4096];
WIN32_FIND_DATA fd;
HANDLE h;
HANDLE fh;
LARGE_INTEGER best_ft;
printf("parse %s\n",dstpath);
best_ft.QuadPart = 0;
sprintf(dstbuf,"%s\\*.*",dstpath);
h = FindFirstFile(dstbuf,&fd);
if (h != INVALID_HANDLE_VALUE)
{
for(;;)
{
if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
if ((strcmp(fd.cFileName,"..") != 0) && (strcmp(fd.cFileName,".") != 0))
{
sprintf(dstbuf,"%s\\%s",dstpath,fd.cFileName);
parse_file2folder_date(dstbuf);
}
}
else
{
LARGE_INTEGER dm_ft;
dm_ft.HighPart = fd.ftLastWriteTime.dwHighDateTime;
dm_ft.LowPart = fd.ftLastWriteTime.dwLowDateTime;
if (dm_ft.QuadPart > best_ft.QuadPart)
{
best_ft.QuadPart = dm_ft.QuadPart;
}
}
if (!FindNextFile(h,&fd)) break;
}
FindClose(h);
}
if (best_ft.QuadPart)
{
HANDLE fh;
fh = CreateFile(dstpath,FILE_WRITE_ATTRIBUTES,0,0,OPEN_EXISTING,FILE_FLAG_BACKUP_SEMANTICS,0);
if (fh != INVALID_HANDLE_VALUE)
{
printf("set ft %s\n",fd.cFileName);
SetFileTime(fh,0,0,(FILETIME *)&best_ft.QuadPart);
CloseHandle(fh);
}
else
{
printf("invalid file handle %s\n",dstbuf);
// ExitProcess(0);
}
}
}
// copy timestamps:
void parse(char *dstpath,char *srcpath)
{
char dstbuf[4096];
char srcbuf[4096];
WIN32_FIND_DATA fd;
HANDLE h;
HANDLE fh;
printf("parse %s %s\n",dstpath,srcpath);
sprintf(srcbuf,"%s\\*.*",srcpath);
h = FindFirstFile(srcbuf,&fd);
if (h != INVALID_HANDLE_VALUE)
{
for(;;)
{
if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
if ((strcmp(fd.cFileName,"..") != 0) && (strcmp(fd.cFileName,".") != 0))
{
FILETIME ft;
HANDLE fh;
sprintf(dstbuf,"%s\\%s",dstpath,fd.cFileName);
fh = CreateFile(dstbuf,FILE_WRITE_ATTRIBUTES,0,0,OPEN_EXISTING,FILE_FLAG_BACKUP_SEMANTICS,0);
if (fh != INVALID_HANDLE_VALUE)
{
printf("set ft %s\n",fd.cFileName);
SetFileTime(fh,&fd.ftCreationTime,0,&fd.ftLastWriteTime);
CloseHandle(fh);
}
else
{
printf("invalid file handle %s\n",dstbuf);
// ExitProcess(0);
}
sprintf(srcbuf,"%s\\%s",srcpath,fd.cFileName);
parse(dstbuf,srcbuf);
}
}
else
{
sprintf(dstbuf,"%s\\%s",dstpath,fd.cFileName);
fh = CreateFile(dstbuf,FILE_WRITE_ATTRIBUTES,0,0,OPEN_EXISTING,FILE_FLAG_BACKUP_SEMANTICS,0);
if (fh != INVALID_HANDLE_VALUE)
{
printf("set ft %s\n",fd.cFileName);
SetFileTime(fh,&fd.ftCreationTime,0,&fd.ftLastWriteTime);
CloseHandle(fh);
}
else
{
printf("invalid file handle %s\n",dstbuf);
// ExitProcess(0);
}
}
if (!FindNextFile(h,&fd)) break;
}
FindClose(h);
}
}
int main(int argc,char **argv)
{
parse_file2folder_date("E:\\Music Albums");
// parse("I:\Backup folder","D:\folder");
Yeah, it's a shame... but we have what we have.void wrote: Thu Aug 29, 2024 12:04 am It's a shame there isn't an NTFS driver level option to do this..
I would like to avoid writing to volumes with Everything.
I think this would be better as a separate tool.
Well, I mentioned in the very first post and even gave the code of a script that performs this function, but as I wrote then - it has the biggest drawback that it doesn't work automatically, but only in manual mode.void wrote: Thu Aug 29, 2024 12:04 am For your use case, I recommend writing a script that traverses the desired folders and sets the filetime of all parents.
Excuse me, where can this code be used? In Everything?void wrote: Thu Aug 29, 2024 12:04 am Here's some C code that I use to update parent folder timestamps and copy timestamps to a backup location:
Does it work automatically?
Do you have C code where we can preserve all 3 date and time fields of a chosen source copy of files and folders to a chosen destination ?void wrote: Thu Aug 29, 2024 12:04 am It's a shame there isn't an NTFS driver level option to do this..
I would like to avoid writing to volumes with Everything.
I think this would be better as a separate tool.
For your use case, I recommend writing a script that traverses the desired folders and sets the filetime of all parents.Is it possible to do this not only via limiting to a specific folder, but more broadly through blacklisting/whitelisting?
Here's some C code that I use to update parent folder timestamps and copy timestamps to a backup location:Code: Select all
#include <stdlib.h> #include <stdio.h> #include <windows.h> // update parent to latest file timestamp: void parse_file2folder_date(char *dstpath) { char dstbuf[4096]; WIN32_FIND_DATA fd; HANDLE h; HANDLE fh; LARGE_INTEGER best_ft; printf("parse %s\n",dstpath); best_ft.QuadPart = 0; sprintf(dstbuf,"%s\\*.*",dstpath); h = FindFirstFile(dstbuf,&fd); if (h != INVALID_HANDLE_VALUE) { for(;;) { if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { if ((strcmp(fd.cFileName,"..") != 0) && (strcmp(fd.cFileName,".") != 0)) { sprintf(dstbuf,"%s\\%s",dstpath,fd.cFileName); parse_file2folder_date(dstbuf); } } else { LARGE_INTEGER dm_ft; dm_ft.HighPart = fd.ftLastWriteTime.dwHighDateTime; dm_ft.LowPart = fd.ftLastWriteTime.dwLowDateTime; if (dm_ft.QuadPart > best_ft.QuadPart) { best_ft.QuadPart = dm_ft.QuadPart; } } if (!FindNextFile(h,&fd)) break; } FindClose(h); } if (best_ft.QuadPart) { HANDLE fh; fh = CreateFile(dstpath,FILE_WRITE_ATTRIBUTES,0,0,OPEN_EXISTING,FILE_FLAG_BACKUP_SEMANTICS,0); if (fh != INVALID_HANDLE_VALUE) { printf("set ft %s\n",fd.cFileName); SetFileTime(fh,0,0,(FILETIME *)&best_ft.QuadPart); CloseHandle(fh); } else { printf("invalid file handle %s\n",dstbuf); // ExitProcess(0); } } } // copy timestamps: void parse(char *dstpath,char *srcpath) { char dstbuf[4096]; char srcbuf[4096]; WIN32_FIND_DATA fd; HANDLE h; HANDLE fh; printf("parse %s %s\n",dstpath,srcpath); sprintf(srcbuf,"%s\\*.*",srcpath); h = FindFirstFile(srcbuf,&fd); if (h != INVALID_HANDLE_VALUE) { for(;;) { if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { if ((strcmp(fd.cFileName,"..") != 0) && (strcmp(fd.cFileName,".") != 0)) { FILETIME ft; HANDLE fh; sprintf(dstbuf,"%s\\%s",dstpath,fd.cFileName); fh = CreateFile(dstbuf,FILE_WRITE_ATTRIBUTES,0,0,OPEN_EXISTING,FILE_FLAG_BACKUP_SEMANTICS,0); if (fh != INVALID_HANDLE_VALUE) { printf("set ft %s\n",fd.cFileName); SetFileTime(fh,&fd.ftCreationTime,0,&fd.ftLastWriteTime); CloseHandle(fh); } else { printf("invalid file handle %s\n",dstbuf); // ExitProcess(0); } sprintf(srcbuf,"%s\\%s",srcpath,fd.cFileName); parse(dstbuf,srcbuf); } } else { sprintf(dstbuf,"%s\\%s",dstpath,fd.cFileName); fh = CreateFile(dstbuf,FILE_WRITE_ATTRIBUTES,0,0,OPEN_EXISTING,FILE_FLAG_BACKUP_SEMANTICS,0); if (fh != INVALID_HANDLE_VALUE) { printf("set ft %s\n",fd.cFileName); SetFileTime(fh,&fd.ftCreationTime,0,&fd.ftLastWriteTime); CloseHandle(fh); } else { printf("invalid file handle %s\n",dstbuf); // ExitProcess(0); } } if (!FindNextFile(h,&fd)) break; } FindClose(h); } } int main(int argc,char **argv) { parse_file2folder_date("E:\\Music Albums"); // parse("I:\Backup folder","D:\folder");
voidhash also has code that resets folder timestamps.
robocopy, fastcopy, turbocopy? ...Do you have C code where we can preserve all 3 date and time fields of a chosen source copy of files and folders to a chosen destination ?
I know about FolderTimeUpdate. Can it automatically do what I asked in my first post?
robocopy does work but have to enter source folder and destination and all options manually or to make robocopy job to exclude certain paths. But, for smple copy is there a C code script or anything equivalent ?therube wrote: Thu Aug 29, 2024 5:35 pmrobocopy, fastcopy, turbocopy? ...Do you have C code where we can preserve all 3 date and time fields of a chosen source copy of files and folders to a chosen destination ?
And I guess this is related, how to automate robocopy with windows copy and paste ?
And there was another thread of yours (that I'm not finding at the moment) where you asked the same question?
And all answers are likely to have gotcha's too.
Code: Select all
Param
(
[Parameter(Mandatory=$true)]
[String]$startfolder
)
# Check if valid path
If ( -not ( Test-Path $startfolder )) {
echo "Not a valid folder: $startfolder"
Exit
}
# Create list of all subfolders and sort descending by name (+ path), so deeper levels come first.
$allfolders = gci -literalpath $startfolder -dir -recurse -force | sort FullName -descending
# For each of these subfolders: check if date of most recent item in this folder is newer than folder date. If so: update folderdate.
$allfolders | % {
$ItemsDate = ''
echo "Current Folder : $($_.FullName) "
$ItemsDate = (gci -Dir -literalpath $_.FullName -force | sort LastWriteTime)[-1].LastWriteTime
echo "Most recent item date = $($ItemsDate)"
echo "FolderDate = $($_.lastWriteTime)"
If ($_.lastWriteTime -lt $ItemsDate) {
echo "FolderDate $_.FullName will be updated"
# $_.lastWriteTime = $ItemsDate
}
}
Code: Select all
folder: sort:dm count:1 columns:dm;name;size;pathChange the following line:Do you have C code where we can preserve all 3 date and time fields of a chosen source copy of files and folders to a chosen destination ?
SetFileTime(fh,&fd.ftCreationTime,0,&fd.ftLastWriteTime);SetFileTime(fh,&fd.ftCreationTime,&fd.ftLastAccessTime,&fd.ftLastWriteTime);