Call exiftool from a python script?

To avoid launching a new process for each image, you should start exiftool using the -stay_open flag. You can then send commands to the process via stdin, and read the output on stdout. ExifTool supports JSON output, which is probably the best option for reading the metadata.

Here’s a simple class that launches an exiftool process and features an execute() method to send commands to that process. I also included get_metadata() to read the metadata in JSON format:

import subprocess
import os
import json

class ExifTool(object):

    sentinel = "{ready}\n"

    def __init__(self, executable="/usr/bin/exiftool"):
        self.executable = executable

    def __enter__(self):
        self.process = subprocess.Popen(
            [self.executable, "-stay_open", "True",  "-@", "-"],
            stdin=subprocess.PIPE, stdout=subprocess.PIPE)
        return self

    def  __exit__(self, exc_type, exc_value, traceback):

    def execute(self, *args):
        args = args + ("-execute\n",)
        self.process.stdin.write(str.join("\n", args))
        output = ""
        fd = self.process.stdout.fileno()
        while not output.endswith(self.sentinel):
            output +=, 4096)
        return output[:-len(self.sentinel)]

    def get_metadata(self, *filenames):
        return json.loads(self.execute("-G", "-j", "-n", *filenames))

This class is written as a context manager to ensure the process is exited if you are done. You can use it as

with ExifTool() as e:
    metadata = e.get_metadata(*filenames)

EDIT for python 3:
To get this to work in python 3 two small changes are needed. The first is an additional argument to subprocess.Popen:

self.process = subprocess.Popen(
         [self.executable, "-stay_open", "True",  "-@", "-"],
         stdin=subprocess.PIPE, stdout=subprocess.PIPE)

The second is that you have to decode the byte series returned by

output +=, 4096).decode('utf-8')

EDIT for Windows: To get this working on Windows, the sentinel need to be changed into "{ready}\r\n", i.e.

sentinel = "{ready}\r\n"

Otherwise the program will hang because the while loop inside execute() won’t stop

Leave a Comment