Skip to content

Instantly share code, notes, and snippets.

@porg
Created January 20, 2022 22:17
Show Gist options
  • Select an option

  • Save porg/b6b3160f41c5c6ce7ced5a4982d4aa2e to your computer and use it in GitHub Desktop.

Select an option

Save porg/b6b3160f41c5c6ce7ced5a4982d4aa2e to your computer and use it in GitHub Desktop.
Copy modification date from all files of source directory to same named files at destination directory if existing there
#!/bin/bash
if [[ -z "$1" || -z "$2" || "$1" == "-h" || "$1" == "--help" ]] ; then
cat <<EOF
USAGE: `basename $0` backupDir targetDir
Loops through all files on root level of backupDir.
For each file in backupDir it tries to find a same named file in targetDir.
- If not found in target (because renamed or removed meanwhile) the file is skipped.
- If found and they have the same modification timestamp nothing is done either.
- If found and the moddates differ the moddate is copied from the backup to the target.
EOF
exit
fi
backupDir=$1
targetDir=$2
if [[ ! -d "$backupDir" ]]
then
echo "Error! No directory at path for backupDir:"
echo "$1"
exit
fi
if [[ ! -d "$targetDir" ]]
then
echo "Error! No directory at path for targetDir:"
echo "$2"
exit
fi
backupFiles=$(ls -l1 "$backupDir")
targetFiles=$(ls -l1 "$targetDir")
backupFileCount=$(echo "$backupFiles" | wc -l)
targetFileCount=$(echo "$targetFiles" | wc -l)
echo
echo "Directories to process:"
echo " Backup:$backupFileCount files at: $backupDir"
echo " Target:$targetFileCount files at: $targetDir"
echo
echo "Output format:"
echo
echo " File Name.ext"
echo " Backup's creation date"
echo " Backup's modification date"
echo " Target's modification date"
echo
echo "Symbol at line start of target's moddate:"
echo
echo "√ Backup and target have same moddate. No need to act."
echo "≠ Target's mod-date differs. --> Gets restored from backup."
echo "x No same named file in target directory anymore."
echo
echo
echo "Copying modification timestamps..."
echo
echo "$backupFiles" | while read file
do
echo " $file"
backupFileCreation=$(GetFileInfo -d "$backupDir/$file")
backupFileModification=$(GetFileInfo -m "$backupDir/$file")
echo " $backupFileCreation"
echo " $backupFileModification ───┐"
if [[ -f "$targetDir/$file" ]] ; then
targetFileModification=$(GetFileInfo -m "$targetDir/$file")
if [[ "$backupFileModification" == "$targetFileModification" ]] ; then
echo "$targetFileModification √ ─┘"
else
SetFile -m "$backupFileModification" "$targetDir/$file"
echo "$targetFileModification └───> Restored from backup √"
fi
else
echo "x Not in target dir ! ─┘"
fi
echo
done
@huyz
Copy link

huyz commented Nov 27, 2024

Thanks for your script. I customized it to fit my style: https://github.com/huyz/trustytools/blob/master/contrib/restore-modified-date.sh

@porg
Copy link
Author

porg commented Nov 27, 2024

Glad that my script could serve as the basis for your need! 👍

Could you outline the main innovations / differences of your script? Am curious… 🧐

  • I only scanned its code superficially, e.g. it seems to check for installed tools, and else installs them via brew.
  • Also curious whether and how you replaced the deprecated GetFileInfo and SetFile.

@huyz
Copy link

huyz commented Nov 27, 2024

Oh I didn't really make any significant changes. The checking for installed tools, getopt, etc.—those are all part of my usual bash templates—nothing specific to your script, really.
There are no innovations.

I didn't replace the deprecated tools. I'll stick with what works for now :)

@huyz
Copy link

huyz commented Nov 28, 2024

Oh actually I did just find a subtle bug in our implementations. My use case is to restore the modification dates of directories, not plain files. But the key here is that the modification dates of deeper subdirectories need to be updated before those of parent subdirectories. That's because any SetFile would not only restore the modification date of a given file or subdirectory but also update the modification date of the parent directory since that's the normal expected behavior of filesystems: if you modify the content of a directory, the directory's modification date is updated. If the restore is done in the wrong order, then any restores would later be overwritten by these automatic updates.

As I said, the solution is to update deep subdirectories before parent directories. Since I'm using the find command, for my script that means I need to use the -depth flag, which means "depth-first traversal". I've just fixed my script. For you it would mean figuring out a way to make ls output in the right order, perhaps by reversing the order by using tac. (Btw, you were asking about "innovations"; I use find instead of ls so that my script can stream an arbitrary number of files and folders. With ls and bash arrays, you may run into hard-coded and/or memory limits).

Of course for many users, they only care about the modification dates of files and not directories, so this subtlety wouldn't matter for them.

@huyz
Copy link

huyz commented Nov 30, 2024

@porg Actually, by demand, I did just port to using gdate, gstat, and gtouch so that there's no reliance on the deprecated GetFileInfo and SetFile tools (and now works for Linux or wherever the GNU coreutils tools are supported)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment