Calabi Labs · Guide · 2026-06-21
ExifTool is the most powerful metadata manipulation tool available—but most users only scratch the surface. This guide covers the advanced workflows, scripting patterns, and edge-case handling that separate casual users from professionals.
Before diving into commands, understand how ExifTool processes files:
This architecture means you can read without risk and craft surgical edits.
```bash
exiftool -gps:all -n -csv photos/ > gps_data.csv
exiftool -a -s -G:1 image.jpg
exiftool -ee -three -Api:all video.mp4 ```
```bash
exiftool -if '$LensID =~ /85mm/' -filename -datesuboriginal .
exiftool -if '$gpslatitude and not $City' -filename -ext jpg . ```
```bash
exiftool -b -ThumbnailImage photo.jpg > thumbnail.jpg
exiftool -b -PreviewImage -w !%d%f_preview.jpg -ext nef . ```
```bash
exiftool -if 'not defined $Copyright' -Copyright="Your Name" .
exiftool -if '$LensModel =~ /Tamron/' -LensModel="Tamron SP 24-70mm" .
exiftool -if '$gpslatitude' -gpslatitude+=0.05 -gpslongitude+=0.03 . ```
```bash
exiftool -alldates+=1:00 photos/
exiftool -DateTimeOriginal="2024:06:15 10:30:00" \ -CreateDate+=0:0:0 2:30:00 \ -ModifyDate+=0:0:0 2:30:00 file.jpg
exiftool "-DateTimeOriginal>FileModifyDate" . ```
```bash
exiftool -gpslatitude=40.7128 -gpslongituderef=W -gpslongitude=74.006 \ -gpslatituderef=N photo.jpg
exiftool -if '$filename =~ /keep_location/' -gps:all= -r photos/
```
```bash
find /photos -type f -name "*.jpg" | parallel -j4 'exiftool -overwrite_original -Artist="Name" {}'
exiftool -r -overwrite_original '-Directory<${directory;s/old/new/}' /source/ ```
```bash
for f in *.JPG; do year=$(echo "$f" | cut -d_ -f1) exiftool -overwrite_original -keywords+="$year" "$f" done
exiftool -b -JpgFromRaw $file | magick - -resize 800x600 thumb.jpg ```
```bash
cat > template.args << 'EOF' -Artist=John Doe -Copyright=© 2024 -Keywords+=Portfolio -XMPToolkit= EOF
exiftool @template.args -r photos/ ```
```bash
exiftool -all= -overwrite_original \ -ICC_Profile:all= \ -XMP-xmpMM:DocumentID= \ -r public_photos/
exiftool -overwrite_original \ -gps:all= \ -exif:all= \ -Make= \ -Model= \ -SerialNumber= \ -OwnerName= \ -ImageUniqueID= \ public_photos/ ```
```bash
exiftool -overwrite_original_in_place \ -Copyright="Studio Name" \ -Artist="Photographer" \ -Credit="Studio Name" \ -Contact="[email protected]" \ -CopyrightNotice="All rights reserved" \ .NEF .CR2 *.ARW
exiftool -ext xmp -r dir/ # Reads/writes sidecar files automatically ```
```bash
exiftool -overwrite_original \ -geotag track.gpx \ -ext mp4 -ext mov .
exiftool -all= -overwrite_original video.mp4
exiftool -MediaCreateDate="2024:01:15 12:00:00" video.mov ```
```bash
exiftool -xmp-dc:Subject-="old-keyword" \ -xmp-dc:Subject+="new-keyword" \ photo.jpg
exiftool -xmp-pkg:ProjectCode="ABC123" \ -xmp-pkg:ClientID="XYZ-789" \ photo.jpg ```
```bash
exiftool -IPTC:Country-PrimaryCodeName=USA \ -IPTC:Province-State="California" \ -IPTC:City=Pasadena \ -IPTC:Sub-location="Old Town" \ photo.jpg
exiftool -common \ -IPTC:Headline="..." \ -XMP-dc:Title="..." \ photo.jpg ```
```python import subprocess import os
def geotag_photos(photo_dir, gpx_file): """Geotag photos from GPX track file.""" for filename in os.listdir(photo_dir): if filename.lower().endswith(('.jpg', '.nef')): filepath = os.path.join(photo_dir, filename) subprocess.run([ 'exiftool', '-overwrite_original', '-geotag', gpx_file, filepath ], check=True)
def extract_for_database(photo_dir, output_csv): """Extract structured metadata for database import.""" subprocess.run([ 'exiftool', '-T', # Tab-separated '-filename', '-DateTimeOriginal', '-CreateDate', '-GPSLatitude', '-GPSLongitude', '-Make', '-Model', '-LensModel', '-ExposureTime', '-FNumber', '-ISO', '-r', # Recursive '-csv', f'>{output_csv}', photo_dir ], check=True) ```
```bash #!/bin/bash
exiftool '-FileName
compare_meta() { echo "=== File 1 ===" exiftool -s "$1" echo "=== File 2 ===" exiftool -s "$2" echo "=== Differences ===" diff <(exiftool -s "$1" | sort) <(exiftool -s "$2" | sort) } ```
```bash
exiftool -all= -overwrite_original *.jpg
exiftool -ignoreMinorErrors -overwrite_original -all= bad_file.jpg
find . -name "*.jpg" -print0 | \ xargs -0 -P 8 -I {} exiftool -overwrite_original -all= {} ```
```bash
exiftool -if 'not $Artist or $Artist ne "Name"' -Artist="Name" .
```
| Issue | Solution |
|---|---|
| Write-protected files | Use -overwrite_original or remove write protection |
| Corrupted metadata | Add -ignoreMinorErrors flag |
| Unsynchronized sidecar files | Use -srcfile with -overwrite_original_in_place |
| Encoding problems | Specify encoding: -charset UTF8 or -charset Latin1 |
| Slow batch processing | Process smaller batches, use -progress to monitor |
```bash
exiftool -restore_original image.jpg
exiftool -restore_original -r .
exiftool -validate -v image.jpg ```
```bash
exiftool -a -s -G:1 file.jpg
exiftool -Tag="Value" file.jpg
exiftool -if '$condition' -Tag="Value" file.jpg
exiftool -r -Tag="Value" /directory/
exiftool -all= -overwrite_original file.jpg
exiftool -geotag track.gpx *.jpg
exiftool -alldates+=1:00 file.jpg
exiftool -csv -filename -date* -gps* . > output.csv
exiftool -b -ThumbnailImage photo.jpg > thumb.jpg
exiftool -overwrite_original -Tag="Value" file.jpg ```
ExifTool's power scales with your use case. Start with one advanced workflow—batch geotagging, privacy scrubbing, or Python integration—and build from there. Bookmark the official ExifTool documentation and tag name database as your constant references.
Try Calabi free at calabilabs.com — 10 cleans, no card.