Concepts / List Methods: join()

List Methods: join()

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().

  • 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.

Key point

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.

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

Two Sources Become One Command Fragment

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

  1. 1
    Start with the list

    The source list contains '/home/user/docs' and '/home/user/photos'.

  2. 2
    Choose the separator

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

  3. 3
    Apply join()

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

  4. 4
    Use the result

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

Result

/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.

Windowsbackup\archive.zipos.sepnative separatortarget archive pathbackup plus timestamp plus.zipmacOS and Linuxbackup/archive.zipuses backslashuses forward slashjoins path parts
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.

Code walkthrough
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")
  1. 1
    Python stores the source directories in a list.
  2. 2
    Python creates a timestamp and combines it with the target directory and .zip extension.
  3. 3
    The join() call creates one string containing the source directories separated by spaces.
  4. 4
    format() constructs the complete zip command.
  5. 5
    print() displays the command before execution.
  6. 6
    os.system() passes the command to the operating system as if it had been typed in a terminal or command prompt.
  7. 7
    The program compares the returned value with zero and prints a success or failure message.
  • source is a list of directories to back up.
  • os.sep supplies the operating system's directory separator.
  • strftime() creates the timestamp text.
  • join() turns the source list into one space-separated string.
  • format() inserts the target and joined sources into the command.
  • os.system() executes the command through the operating system shell.
Output
The printed command contains zip -r, the timestamped target path, and the two source paths. The later message depends on the integer returned by os.system().
Python programbuild commandOperating systemshellreceive commandzip utilitycreate archivetimestamped archive.zip filereturn value0 or non-zero integeros.system(command)run commandwrite archivereport resultcheck result
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.

The backup program interprets the value returned by os.system().

Mistakes Beginners Make

  • Common mistake

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

    What it looks like
    command = "zip -r {0} {1}".format(target, source)
    Why it fails

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

    Do this instead

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

  • Common mistake

    Hardcoding one directory separator.

    What it looks like
    target = target_dir + "/" + timestamp + ".zip"
    Why it fails

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

    Do this instead
    Use target_dir + os.sep + timestamp + ".zip".
  • Common mistake

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

    What it looks like
    os.system(command)
    print("Backup succeeded")
    Why it fails

    The command may return a non-zero error code.

    Do this instead

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

  • Common mistake

    Using a fixed archive filename.

    What it looks like
    target = target_dir + os.sep + "backup.zip"
    Why it fails

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

    Do this instead

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

Best practice

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. 1
    Identify the source list and the target directory.
  2. 2
    Use time.strftime() to create the timestamp text.
  3. 3
    Use os.sep when combining the target directory and timestamped filename.
  4. 4
    Use ' '.join(source) to create the source portion of the command.
  5. 5
    Use format() to combine the zip utility, options, target, and joined sources.
  6. 6
    Pass the command to os.system().
  7. 7
    Compare the returned value with zero and report success or failure.

The Complete Mental Model

  1. 1
    join() turns the list of source directories into one space-separated string for the command.
  2. 2
    os.sep keeps the target path aligned with the operating system's directory separator.
  3. 3
    time.strftime() creates a timestamped archive name using date and time format codes.
  4. 4
    format() assembles the zip command, and os.system() sends it to the operating system shell.
  5. 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.

Related concepts

More concepts starting with L