Copy/Paste entire folder with all files created as empty

I want to be able to copy an entire folder with its substructure to a new location, with all the existing files recreated as empty files. I now use "Copy File Names" on a selection of files, and then "Paste File List" in the new location. It works fine, but if there are 10 subfolders, it is a lot of repetitive action... Things would be even better if attributes and file names could be changed at the same time (for instance change all occurrences in filenames from 2013 to 2014, and set all dates/times to 01/01/2014 / 00:00).

What exactly do you mean by "empty files"? What kind of files are they?

they are 0-byte files with the same name as the copied files - it's explained here: [url]https://resource.dopus.com/t/paste-file-list/6591/1]
what I want to do is exactly that, but I want to do it for a complete file tree or folder structure, and not just for a number of files in one specific folder.

You could use the version of the script you linked to which takes full paths, and then copy all the paths from the old directory structure to the clipboard, paste into a text editor, search & replace to change the start of them to what you want it to be, and then run the script.

Would that do the job?

It would, BUT: the script does not seem to create directories, so there is no action for files on the clipboard that point to unexisting directories; so you first have to create empty directories (and make sure they are identical to the paths in the intended filenames) and then do as you suggested. Also, I found that if any source folder contains only ONE file, the script will fail to create an empty file for that name.

In addition, you have to repeat the entire action (Copy Full Pathnames, Paste File list) for every subfolder in your text file, as the script seems to create files for the first folder in the clipboard only, and then ignores all files in subsequent folders. With complex trees, this quickly gets very cumbersome...

Looking at the script, it doesn't create directories at all so it wasn't a good suggestion on my part.

It could be combined with the Recreate Empty Directory Structure button, so one creates the folders and the other creates the files, but that'd be quite a tedious process if it's something you do a lot. (Although if it is a fixed set of data, you only need to do it once to a template directory, and you can then copy the template dir each time you need a new version of it.)

I think you'll need some custom scripting to get exactly what you want in a single button.

[Update: The newer Paste Empty File List script can create directories.]

I know this is an old topic, but I wanted to point out a tool I'd gotten made for exactly this purpose. The benefit is this is a very portable solution, as it produces as zip archive you can use elsewhere. see here
http://skwire.dcmembers.com/fp/?page=zero-zipper
This is slow though, I've written a python script which is far faster shown below:

#!/usr/bin/python
import os, sys, zipfile, optparse, zlib, fnmatch, time, tempfile, time

SUFFIXES = {1000: ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'],
            1024: ['B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB']}

def humanReadableByteCount(size, a_kilobyte_is_1024_bytes=True):
    '''Convert a file size to human-readable form.

    Keyword arguments:
    size -- file size in bytes
    a_kilobyte_is_1024_bytes -- if True (default), use multiples of 1024
                                if False, use multiples of 1000

    Returns: string
    from http://getpython3.com/diveintopython3/your-first-python-program.html#divingin
    '''
    if size < 0:
        raise ValueError('number must be non-negative')

    multiple = 1024 if a_kilobyte_is_1024_bytes else 1000
    for suffix in SUFFIXES[multiple]:
        if size < multiple:
            return '{0:.1f} {1}'.format(size, suffix)
        size = float(size) / multiple

    raise ValueError('number too large')

def zfAddNullFile(zf, arcname, date_time, extattr=0):
  """ Adapted from the method in zipfile """
  arcname = os.path.normpath(os.path.splitdrive(arcname)[1])
  arcname = arcname.lstrip(os.sep + (os.altsep or ""))
  zinfo = zipfile.ZipInfo(arcname, date_time)
  zinfo.external_attr = extattr
  zinfo.compress_type = zf.compression
  zinfo.file_size = 0
  zinfo.flag_bits = 0x00
  zinfo.header_offset = zf.fp.tell()    # Start of header bytes
  zf._writecheck(zinfo)
  zf._didModify = True
  zinfo.CRC = CRC = zlib.crc32("")
  zinfo.compress_size = 0
  zinfo.file_size = 0
  zf.fp.write(zinfo.FileHeader())
  zf.filelist.append(zinfo)
  zf.NameToInfo[zinfo.filename] = zinfo
  
def printFilename(f, msg=None):
  if not options.quiet:
    if msg:
      print msg,
    
    try:
      print f
    except:
      print f.encode("charmap", "replace")

if __name__ == '__main__':
  start_time = time.clock()
  usage = """%prog [options] <target-zip> <source-dir> [<source-dir...>]
    -m, --exclude_mask, Exclude mask
    -q, --quite, Quiet mode
    -e, --omit_empty, Omit empty directories
    -z, --rezip, Do not rezip the target to make it smaller
    -x, --extract, Extracts the zipfile and all zipfiles within it. 
    
    The <target-zip> will be overwritten if it exists. 
    This program will create a <target-zip> of all the filenames ONLY, no data from the files 
    (retaining directory structure) in the provided <source directories>. 
    
    The intent is to allow you to recreate a FILE & DIRECTORY structure in another location
    without all the content and data. 
  """

  optp=optparse.OptionParser(usage=usage)
  optp.add_option("-m", help="exclude mask", action="append", dest="exclude_mask", default=[])
  optp.add_option("-q", help="be quiet", action="store_true", dest="quiet", default=False)
  optp.add_option("-e", help="omit empty directories", action="store_true", dest="omit_empty", default=False)
  optp.add_option("-z", help="don't rezip the target to make it smaller", action="store_false", dest="rezip", default=True)
  optp.add_option("-x", help="Extracts the zipfile and all zipfiles within it.", action="store_true", dest="extract", default=False)
  optp.add_option("-d", help="Debug.", action="store_true", dest="debug", default=False)
  err=optp.error

  options, args = optp.parse_args()

  if len(args)<2:
    optp.print_usage()
    sys.exit(1)
  else:
    #Basic input validation
    # Check if file exists? Warn about overwriting
    target_zip = os.path.abspath(args[0])
    if options.rezip:
      intermediate_zip = target_zip.replace(".zip","zip.zip")
    else:
      intermediate_zip = target_zip
    for source in args[1:]:
      try:
        source_dir=source.decode("latin-1")
      except:
        print "Exception while trying to process directory: %s" % (source)
    if options.debug:
      print target_zip
      print intermediate_zip
      print source_dir

  # EXTRACT 
  if options.extract:
    """ Extracts the zipfile and all zipfiles in it larger than 0 bytes """
    try:
      zf=zipfile.ZipFile(target_zip, "r")
    except IOError, e:
      print e
      sys.exit(1)

    for info in zf.infolist():
      if info.filename.endswith("zip.zip") and info.file_size > 0:
        zf.extract(info)
        zf2=zipfile.ZipFile(info.filename,"r")
        zf2.extractall(source_dir)
        zf2.close()
        os.remove(info.filename)
    zf.close()

    print "Files extracted successfuly to %s" % (source_dir)
    # for files in os.listdir(source_dir):
    #   if files.endswith(".zip"):
    #     zf=zipfile.ZipFile(files, "r")
    #     zf.extractall(source_dir)
    #     zf.close()
        
    sys.exit(0)

  # MAKE TREE ZIP
  zf=zipfile.ZipFile(intermediate_zip, "w")
  filecount = 0

  for sourceDir in args[1:]:
    try:
      sourceDir=sourceDir.decode("latin-1")
    except:
      print sourceDir
      print "Exception while trying to process directory"
    for dp, dn, fn in os.walk(sourceDir):
      if fn:
        for f in fn:
          f=os.path.join(dp, f)
          ok=True
          for xmask in options.exclude_mask:
            if fnmatch.fnmatch(f, xmask):
              ok=False
              break
          if ok:
            try:
              mtime=time.localtime(os.stat(f).st_mtime)
            except ValueError:
              print "Error: Modified time out of range."
              printFilename(f)
              print os.stat(f).st_mtime
              mtime=time.localtime(0) #set time to unix epoch 0 = 'Thu Jan 01 07:00:00 1970'
            except WindowsError:
              print "Error: Can't find file due to windows limitations."
              printFilename(f)
              mtime=time.localtime(0) #set time to unix epoch 0 = 'Thu Jan 01 07:00:00 1970'
              
            zfAddNullFile(zf, f, (mtime.tm_year, mtime.tm_mon, mtime.tm_mday, mtime.tm_hour, mtime.tm_min, mtime.tm_sec))
            filecount += 1
      elif not options.omit_empty:
        mtime=time.localtime(os.stat(dp).st_mtime)
        #printFilename(dp, "(empty directory)")
        zfAddNullFile(zf, dp, (mtime.tm_year, mtime.tm_mon, mtime.tm_mday, mtime.tm_hour, mtime.tm_min, mtime.tm_sec), 16)
  msg = "Zip file created successfuly in %.2f seconds with %d files." % (time.clock()-start_time, filecount)  
  zf.comment = msg
  zf.close()

  if options.rezip:
    #tf = tempfile.NamedTemporaryFile()
    zf=zipfile.ZipFile(target_zip, "w", zipfile.ZIP_DEFLATED)
    zf.write(intermediate_zip,arcname=os.path.basename(intermediate_zip))
    zf.comment = msg
    zf.close()
    os.remove(intermediate_zip)
    #os.rename(tf.name,target_zip)
  
  print msg
  print target_zip + " " + humanReadableByteCount(os.stat(target_zip).st_size)
  
1 Like