Using Python's os Module for File Operations
The organization has three levels: a main backup directory, a date-based subdirectory, and a time-stamped zip file inside that subdirectory.
From Clutter to Structure
A backup script can work correctly and still become difficult to use. If every archive is stored in one directory with a long timestamped filename, repeated backups can produce dozens of similarly named files. A hierarchical naming scheme improves this situation by dividing the backups first by date and then by time.
The organization has three levels: a main backup directory, a date-based subdirectory, and a time-stamped zip file inside that subdirectory.
The Three-Level Backup Path
The main directory is the common home for all backups. Each date-based subdirectory groups backups made on one day. Each zip file inside that date directory represents a backup made at a particular time. This makes it easier to navigate to a day and then distinguish the backups created during that day.
Reading a Backup Path
Interpret the path /Users/swa/backup/20240115/143022.zip.
- 1Main directory
backup is the main directory that contains the organized backups.
- 2Date directory
20240115 is the date-based directory. The source uses this form for January 15, 2024.
- 3Time-stamped file
143022.zipis the zip archive named with the time 14:30:22.
The path identifies a backup made on January 15, 2024, at 2:30:22 PM.
Turning Time into Names
The time.strftime function returns a formatted string representing the current date or time. In this backup design, one format produces the date directory name and another produces the zip filename. The format code %Y%m%d produces a year-month-day string such as 20240115, while %H%M%S produces a 24-hour time string such as 143022.
Checking Before Creating
Before saving an archive into the date-based directory, the script must determine whether that directory already exists. os.path.exists performs the check. If the directory exists, the script can use it. If it does not exist, os.mkdir creates it. This check matters when the script runs repeatedly, because a second backup on the same day should use the existing date directory rather than attempt to create it again.
import os
import time
backup_dir = '/Users/swa/backup'
date_name = time.strftime('%Y%m%d')
date_dir = backup_dir + '/' + date_name
if not os.path.exists(date_dir):
os.mkdir(date_dir)- 1The
backup_dirvalue names the main backup directory. - 2
time.strftime('%Y%m%d')creates the date-based directory name. - 3The date directory path combines the main directory and the date name.
- 4
os.path.existschecks whether that path already exists. - 5
os.mkdirruns only when the date directory does not exist.
What do you think happens?
If the script runs again on the same day after the date directory has already been created, which branch should run?
Reveal answer
The existing-directory branch
os.path.exists reports that the date directory is already present, so os.mkdir is not needed for that run.
Building the Archive Command
After the directory and names are ready, the script constructs a system command for creating the compressed archive. The command contains the target zip path and the source paths to be archived. The resulting command is passed to os.system, which executes it as an operating-system command.
os.system executes the supplied shell command and returns 0 when the command succeeds. A non-zero return value indicates failure, so the script can use the result to verify whether the backup completed.
Tracing a Second Backup
Imagine that the script runs on January 15, 2024, at 2:30:22 PM and then runs again on the same day at 6:05:00 PM. The date string remains the same, so both backups belong in the 20240115 directory. The time string changes, so the second backup receives a different time-stamped zip filename.
Following the Second Run
Determine what happens when the backup runs on January 15, 2024, at 18:05:00 after a backup has already run earlier that day.
- 1Format the date
The %Y%m%d format produces 20240115 again.
- 2Check the directory
The 20240115 directory already exists because an earlier backup used it.
- 3Format the time
The %H%M%S format produces 180500 for 18:05:00.
- 4Create the new target
The new archive is placed inside the existing date directory with the name
180500.zip.
The second run reuses 20240115 and adds another time-stamped archive inside it.
Mistakes Beginners Make
Common mistake Creating the date directory every time
What it looks likeCalling os.mkdir without first checking whether the date directory exists.
Why it failsA repeated run on the same day may encounter a directory that was already created by an earlier run.
Do this insteadUse os.path.exists first and call os.mkdir only when the directory is absent.
Common mistake Putting the full timestamp into one flat filename
What it looks likeKeeping every backup in the main directory with a long filename containing the complete timestamp.
Why it failsRepeated backups can make the directory cluttered with similarly named files, making a particular day harder to find.
Do this insteadUse the date portion for a subdirectory and the time portion for the zip filename.
Common mistake Ignoring the result from os.system
What it looks likeExecuting the command and never examining the returned value.
Why it failsThe script loses the supplied success or failure signal for the archive operation.
Do this insteadCheck whether the returned value is 0 for success or non-zero for failure.
Common mistake Mixing up the date and time formats
What it looks likeUsing the time format for the directory name or the date format for the archive name.
Why it failsThe intended hierarchy uses the date to group backups and the time to distinguish archives within that group.
Do this insteadUse %Y%m%d for the date directory and %H%M%S for the zip filename.
Practical Design Notes
Treat the backup path as a sequence of decisions rather than as one long filename. First produce the date and time strings. Next prepare the date directory by checking and, when necessary, creating it. Then form the target zip path, construct the command with the source paths, execute it with os.system, and inspect the return value.
Practice the Trace
Suppose the main backup directory is backup, the date string is 20240115, the time string is 180500, and the source is notes. Describe the date directory, the target zip filename, and the order of operations the script should follow.
Hints
- The date string names the subdirectory.
- The time string names the zip file.
- Remember to check for the date directory before creating it.
- The final command uses the target path and the source path.
Practice Solution
Use the supplied names to describe the resulting backup organization.
- 1Build the date directory
Combine backup and 20240115 to identify the date-based directory backup/20240115.
- 2Build the archive name
Add the time string and the zip extension to obtain
180500.zip. - 3Check the directory
Use
os.path.existson the date directory. Callos.mkdironly if it is absent. - 4Construct the command
Use the target archive path and notes as the inputs to the zip command, then pass the command to
os.system.
The target archive is backup/20240115/180500.zip, and the directory check happens before the archive command runs.
Key Takeaways
- 1A hierarchical backup path uses a main directory, a date-based subdirectory, and a time-stamped zip file.
- 2
time.strftime('%Y%m%d')supplies the date directory name, whiletime.strftime('%H%M%S')supplies the time-based archive name. - 3
os.path.existschecks whether the date directory is already present. - 4
os.mkdircreates the date directory only when the check shows that it is absent. - 5
os.systemruns the zip command, and its return value is 0 for success and non-zero for failure.
At a glance
- Hierarchical file naming makes repeated backups easier to locate and verify.
- Date strings organize backups into subdirectories, while time strings distinguish archives made on the same day.
- Checking with
os.path.existsbefore callingos.mkdirallows the script to handle repeated runs. - The final zip command combines a destination path with source paths and runs through
os.system. - The return value from
os.systemgives the script a way to identify success or failure.