Concepts / List Methods: join()

List Methods: join()

A backup program automates file archiving by using Python to construct and execute a zip command with a timestamped filename.

  • Programming

From Manual Backups to Automation

Backing up files manually is tedious and error-prone. You might forget a directory, overwrite an earlier backup, or use an inconsistent filename. A Python backup program automates the process by selecting source directories, creating a timestamped archive name, assembling a zip command, and executing that command.

The central join() operation is small but important: it converts several source-path strings into one space-separated string that can become part of the command passed to os.system().

What do you think happens?

The source list contains two paths. What should the command fragment look like after joining the list with a single space?

  • ['/home/user/docs', '/home/user/photos']
  • /home/user/docs /home/user/photos
  • /home/user/docs,/home/user/photos
Reveal answer

Answer: /home/user/docs /home/user/photos

The expression ' '.join(source) places one space between the list items and returns one string.

Joining Command Components

Suppose source is a list containing the directories to back up. The expression ' '.join(source) uses a space as the separator and returns one string containing every source item in list order. For example, if source contains '/home/user/docs' and '/home/user/photos', the result is '/home/user/docs /home/user/photos'. The list remains useful while Python is selecting the sources; join() creates the single text fragment needed when the command is assembled.

join with spacesinsert as sourcesexecutesourcedocs, photosjoined sourcesdocs photoszip commandutility, options, target,sourcesos.system()command string
How do separate source paths become one command string passed to os.system()?

Two Sources Become One Command Fragment

Combine two source directories so they can be inserted into a zip command.

Start with the list: The source list contains '/home/user/docs' and '/home/user/photos'.

Choose the separator: The string ' ' is the separator, so join() places one space between the items.

Apply join(): The expression ' '.join(source) produces '/home/user/docs /home/user/photos'.

Use the result: That one string becomes the source portion of the command assembled for os.system().

/home/user/docs /home/user/photos

Portable Archive Names

The program builds the target archive filename in stages. It combines the backup directory, os.sep, a timestamp, and the .zip extension. os.sep supplies the directory separator for the operating system: a backslash on Windows and a forward slash on macOS and Linux. This avoids hardcoding one operating system's separator.

uses backslashuses forward slashjoins path partsWindowsbackup\archive.zipos.sepnative separatortarget archive pathbackup plus timestamp plus.zipmacOS and Linuxbackup/archive.zip
How does the same path-building idea use a different separator on Windows compared with macOS and Linux?

The timestamp comes from time.strftime(). The format string '%Y%m%d%H%M%S' represents year, month, day, hour, minute, and second. A resulting name can look like 20240115143022.zip. The timestamp is inserted between the backup directory and the .zip extension, giving the archive a name based on when it was created.

python

From Python to the Shell

After the target filename is ready, the program constructs the full zip command as a string. The command combines the zip utility, options, the target archive, and the joined source list. The -r option tells zip to work recursively through subdirectories. The format() method supplies the target and joined sources for the command's placeholders.

import os import time source = ["/home/user/docs", "/home/user/photos"] target_dir = "/home/user/backups" timestamp = time.strftime("%Y%m%d%H%M%S") target = target_dir + os.sep + timestamp + ".zip" command = "zip -r {0} {1}".format(target, " ".join(source)) print(command) result = os.system(command) if result == 0: print("Backup succeeded") else: print("Backup failed")

os.system(command)run commandwrite archivereport resultcheck resultPython programbuild commandOperating systemshellreceive commandzip utilitycreate archivetimestamped archive.zip filereturn value0 or non-zero integer
How does control and data move from Python code through the shell, zip creation, and the returned integer?
Return valueProgram interpretation
0The zip command succeeded; print the success message.
Non-zero integerThe zip command failed; print the failure message.

Mistakes Beginners Make

  • Passing the source list directly into the command instead of joining it.

    The command needs the source list represented as one command string with spaces between its items.

    Fix: Use " ".join(source) before inserting the sources into the command.

  • Hardcoding one directory separator.

    The source emphasizes that Windows uses a backslash while macOS and Linux use a forward slash.

    Fix: Use target_dir + os.sep + timestamp + ".zip".

  • Treating every os.system() return value as success.

    The command may return a non-zero error code.

    Fix: Store the returned value and compare it with zero before reporting success.

  • Using a fixed archive filename.

    A fixed name does not provide the timestamped naming scheme used by the backup program.

    Fix: Create the timestamp with time.strftime() and place it before the .zip extension.

Customize source and target_dir for your own system before running the program. The source material shows both Windows and Unix-style path examples, so choose paths appropriate to the operating system where the program will run.

Practice the Trace

MEDIUM

Assume source contains two directories and target_dir identifies the backup directory. Trace these operations in order: create the timestamp, build target with os.sep, join source with a space, construct the zip command, call os.system(), and interpret the returned value. Write down the value of each major string before moving to the next step.

Hints
  • The timestamp format is '%Y%m%d%H%M%S'.
  • The joined source string has one space between the source paths.
  • A return value of 0 means success; a non-zero value means failure.
  1. Identify the source list and the target directory.
  2. Use time.strftime() to create the timestamp text.
  3. Use os.sep when combining the target directory and timestamped filename.
  4. Use ' '.join(source) to create the source portion of the command.
  5. Use format() to combine the zip utility, options, target, and joined sources.
  6. Pass the command to os.system().
  7. Compare the returned value with zero and report success or failure.

The Complete Mental Model

  1. join() turns the list of source directories into one space-separated string for the command.
  2. os.sep keeps the target path aligned with the operating system's directory separator.
  3. time.strftime() creates a timestamped archive name using date and time format codes.
  4. format() assembles the zip command, and os.system() sends it to the operating system shell.
  5. The returned value is checked: zero indicates success, while a non-zero error code indicates failure.

Key Takeaways

  • Use ' '.join(source) when a list of source paths must become one space-separated command fragment.
  • Build portable archive paths with os.sep instead of hardcoding a separator.
  • Use time.strftime() to place year, month, day, and time information in the archive filename.
  • Trace the command from Python through os.system() to the zip utility and back to the returned integer.
  • Treat 0 as success and a non-zero return value as failure.