1

I have a situation where a backup has correct Date Modified and other correct metadata. A large volume of these files were copied accidentally without the correct rsync flags (eg, rsync -a does not preserve proper metadata). I can copy them back off with rsync -rlAXtgoDivv --chown=username:staff --fake-super SOURCE DESTINATION which will restore correct metadata, however some of the files have been in use and I don't want to overwrite newer / more recently modified files.

What methods exist to copy only metadata (Date Modified, etc.) over top of files that have identical file contents?

In other words, I'd like to somehow compare file contents and then if they are the same, simply overwrite the metadata so that it has the older / more accurate metadata intact.

8
  • 1
    Things have definitions and the use of “metadata” here would be incorrect. In this case, it called “file attributes” and from your comment, you want to copy over the just the timestamps of creation and modified dates. This is not possible. You would need to evaluate each file and set the editable attribues per your requirements.
    – Allan
    Jun 4, 2023 at 17:17
  • 1
    Thanks for your feedback. Appreciate your perspective & understand your point about the distinction between 'metadata' & 'file attributes'. I've been using the term 'metadata' in a broad sense to include all descriptive information about a file, including its timestamps, which the filesystem maintains for over 30 years in CS. I understand that 'file attributes' might be a more specific term for this context. Re: copying timestamps, I've found it is indeed possible through evaluating each file and setting the attributes as needed. I'll share my results when I have time to finish it up here.
    – ylluminate
    Jun 4, 2023 at 17:29
  • 1
    Metadata = “data that describes other data”. This is data that describes a file thus the distinction. As for copying, there’s no built in utility that will only copy attributes from one to the next. You can however, script the evaluation of said files and set the attributes as necessary.
    – Allan
    Jun 4, 2023 at 17:35
  • 1
    Date file attributes are very similar to metadata. From the question, it's really clear what you're asking and I personally don't think there's a need to replace metadata by date file attributes.
    – Thinkr
    Jun 4, 2023 at 17:56
  • 2
    No @Thinkr. Here’s a primer on metadata. Using an MP3 as an hypothetical example, metadata would describe the music, whereas the file attributes describe the file (container). There’s a big distinction here.
    – Allan
    Jun 4, 2023 at 18:20

1 Answer 1

1

I worked up the following Ruby script to copy old date attributes for each file and folder over to the destination path matching files and folders:

(Saved as ~/bin/copy_dates.rb; nano ~/bin/copy_dates.rb and paste in)

#!/usr/bin/env ruby

# We require the "find" and "fileutils" libraries to work with files and directories
require 'find'
require 'fileutils'

# Retrieve the source and destination root directories from the command line arguments
src_root = ARGV[0]
dst_root = ARGV[1]

# Count the total number of files in the source root directory
total_files = Dir[File.join(src_root, '**', '*')].count { |file| File.file?(file) }
# Initialize a counter for the current file being processed
current_file = 0

# Traverse through all the files in the source root directory
Find.find(src_root) do |src|
  # Replace the source root path with the destination root path to create the destination file path
  dst = src.sub(/^#{Regexp.escape(src_root)}/, dst_root)

  # Check if the source file exists - for both folders and files:
  if File.exist?(src)
    begin
      # Retrieve the access time (atime) and modification time (mtime) of the source file
      src_atime = File.atime(src)
      src_mtime = File.mtime(src)
      # Use the `stat` command to retrieve the birth or creation time (btime) of the source file
      src_btime = `/usr/bin/stat -f "%SB" -t "%m/%d/%Y %H:%M:%S" "#{src}"`.chomp

      # Retrieve the access time (atime), modification time (mtime), and birth time (btime) of the destination file
      dst_atime = File.atime(dst)
      dst_mtime = File.mtime(dst)
      dst_btime = `/usr/bin/stat -f "%SB" -t "%m/%d/%Y %H:%M:%S" "#{dst}"`.chomp

      # Check if the source and destination file timestamps match
      if src_atime == dst_atime && src_mtime == dst_mtime && src_btime == dst_btime
        puts "[#{current_file}/#{total_files}] #{src}: Skipped - timestamps match"
      else
        # If the timestamps don't match, update the access time and modification time of the destination file
        File.utime(src_atime, src_mtime, dst)
        # Use the `SetFile` command to update the birth time of the destination file
        system("SetFile -d '#{src_btime}' '#{dst}'")
        puts "[#{current_file}/#{total_files}] #{src}: Updated - timestamps copied"
      end

    # If an error occurs while processing a file, catch the exception and print an error message
    rescue Exception => e
      puts "Error processing #{src}: #{e}"
    end
  end

  # Increment the current file counter
  current_file += 1
end

Remember to make the file executable and call it as in the following example:

chmod +x ~/bin/copy_dates.rb
copy_dates.rb /source/path/to/folder /destination/path/to/folder

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .