9

I can't figure out how to use shutil.make_archive to zip a folder into a zip-file and then put that saved_20170721.zip file into the folder named past_data

I have the code:

from shutil   import make_archive
from datetime import datetime

folderpath_to_zip_up = 'c:\my_work\todays_data'                    # I wanna zip up this folder
folderpath_archive   = 'c:\past_data'                              # And put it into here as file
filename             = 'saved_{:%Y-%m-%d}'.format(datetime.now())    

make_archive(filename, 'zip', folderpath_archive, folderpath_to_zip_up)

My goal is for 'c:\past_data' to look like:

past_data---+ saved_20170721.zip
            + saved_20170722.zip
            + saved_20170723.zip
            + saved_20170724.zip

But I can't understand the documentation and I keep getting weird results.

user1367204
  • 4,549
  • 10
  • 49
  • 78
  • In the paths, try to replace all the `\` with `/`. If that doesn't work then use `\\` instead. – janos Jul 21 '17 at 19:24

3 Answers3

11

I ran into this same issue and found the docs for shutil.make_archive very dense and counter intuitive.

I eventually got it and wrote a function to help me out because I could never get it right the first time.

import os, shutil
def make_archive(source, destination):
        base = os.path.basename(destination)
        name = base.split('.')[0]
        format = base.split('.')[1]
        archive_from = os.path.dirname(source)
        archive_to = os.path.basename(source.strip(os.sep))
        shutil.make_archive(name, format, archive_from, archive_to)
        shutil.move('%s.%s'%(name,format), destination)

make_archive('/path/to/folder', '/path/to/folder.zip')

Hope it helps.. I also have a blog post about it here

http://www.seanbehan.com/how-to-use-python-shutil-make_archive-to-zip-up-a-directory-recursively-including-the-root-folder/

seanbehan
  • 1,463
  • 15
  • 23
  • 5
    You don't need the final call to `move` if you just put the destination as the first argument to `make_archive`. From the current docs: https://docs.python.org/3/library/shutil.html#shutil.make_archive — "base_name is the name of the file to create, including the path..." – Michael May 07 '21 at 17:43
  • 1
    "including the path": You pretty most saved my day! it so easy to overlook… – DevOps Craftsman Jan 24 '22 at 15:58
  • It doesn't work if the file includes a "." in the name – Xaalek Mar 10 '22 at 15:46
3

The docs are not worded helpfully. If we go from the usage shutil.make_archive(base_name, format[, root_dir[, base_dir[, verbose[, dry_run[, owner[, group[, logger]]]]]]])

  • base_name is the path where you want the archive to be created, including the filename, but not including the extension
  • format, as you figured out, is the type of archive you want to create

You might have noticed that everything else is optional. With just those two parameters, the function will create an archive of your current working directory. That's not what you want, so let's check out the next two parameters:

  • root_dir is the directory from which you want to create an archive
  • base_dir is a way to filter the contents of root_dir. if you set base_dir, your archive will contain the parent directories of base_dir going up to root_dir, but all other contents of those parent directories will not be present. Only base_dir and its children will contain all their contents.

Since you're just trying to archive your directory as is, you don't need base_dir. So I would just amend your original call to:

import os
#...
make_archive(os.path.join(folderpath_archive, filename), 'zip', folderpath_to_zip_up)
Matthias
  • 9,817
  • 14
  • 66
  • 125
ibadibam
  • 169
  • 8
0

I actually found the make_archive example in the shutil documentation most helpful. Here it is for posterity:

Note, the following is not my own work, but is instead copied from the python documentation.

Archiving example

In this example, we create a gzip’ed tar-file archive containing all files found in the .ssh directory of the user:

>>> from shutil import make_archive
>>> import os
>>> archive_name = os.path.expanduser(os.path.join('~', 'myarchive'))
>>> root_dir = os.path.expanduser(os.path.join('~', '.ssh'))
>>> make_archive(archive_name, 'gztar', root_dir)
'/Users/tarek/myarchive.tar.gz'

The resulting archive contains:

$ tar -tzvf /Users/tarek/myarchive.tar.gz
drwx------ tarek/staff       0 2010-02-01 16:23:40 ./
-rw-r--r-- tarek/staff     609 2008-06-09 13:26:54 ./authorized_keys
-rwxr-xr-x tarek/staff      65 2008-06-09 13:26:54 ./config
-rwx------ tarek/staff     668 2008-06-09 13:26:54 ./id_dsa
-rwxr-xr-x tarek/staff     609 2008-06-09 13:26:54 ./id_dsa.pub
-rw------- tarek/staff    1675 2008-06-09 13:26:54 ./id_rsa
-rw-r--r-- tarek/staff     397 2008-06-09 13:26:54 ./id_rsa.pub
-rw-r--r-- tarek/staff   37192 2010-02-06 18:23:10 ./known_hosts

Archiving example with base_dir

In this example, similar to the one above, we show how to use make_archive(), but this time with the usage of base_dir. We now have the following directory structure:

$ tree tmp
tmp
└── root
    └── structure
        ├── content
            └── please_add.txt
        └── do_not_add.txt

In the final archive, please_add.txt should be included, but do_not_add.txt should not. Therefore we use the following:

>>> from shutil import make_archive
>>> import os
>>> archive_name = os.path.expanduser(os.path.join('~', 'myarchive'))
>>> make_archive(
...     archive_name,
...     'tar',
...     root_dir='tmp/root',
...     base_dir='structure/content',
... )
'/Users/tarek/my_archive.tar'

Listing the files in the resulting archive gives us:

$ python -m tarfile -l /Users/tarek/myarchive.tar
structure/content/
structure/content/please_add.txt
winni2k
  • 1,460
  • 16
  • 19