Skip to content

Instantly share code, notes, and snippets.

@Krzysiu
Last active January 28, 2026 00:59
Show Gist options
  • Select an option

  • Save Krzysiu/b71c461e5aacddc54e425da5aa986f74 to your computer and use it in GitHub Desktop.

Select an option

Save Krzysiu/b71c461e5aacddc54e425da5aa986f74 to your computer and use it in GitHub Desktop.
Important exiftool commands - both basic and advanced
# ==============
# TECHNICAL NOTE
# ==============
# When there's a part which you should change to your enviroment (like directory), it's marked as eg. {$PATH}.
# In conditions or print output format, where tags are marked with $ on the beggining, like $DateTimeOriginal, I'm just
# leaving example tag name, so it won't confuse y'all with "${$FOO}"
#
# For Windows give a try alternative ExifTool build: https://oliverbetz.de/pages/Artikel/ExifTool-for-Windows
# If there's something you miss, feel free to ask in comments. Or google (or duckduckgo, whatever) for it.
# Or ask on the official forums - https://exiftool.org/forum/index.php - that's one of the most friendly places ever
# and answers are usually very fast, often from creator himself or some other professional.
# If command differ for Windows and Linux, it's marked as WIN or LIN
# Every command ends with ".". It's where it should look for files. If it's current directory, use ".",
# if path is different, use e.g. "c:\photo\."
# If there's percent sign (%) in command line, it will work in Linux and Windows command line. For Windows
# batch file, change it to double percent (%%)
# Get photos made in the same month/day, but different year (a.k.a. Facebook Memories)
exiftool -r -d "%m-%d" -if '$DateTimeOriginal =~ /'"$(date +%m-%d)"' /' -filename . # LIN
exiftool -r -d "%m-%d" -if "$DateTimeOriginal =~ /{$MONTH-$DAY}/" -filename . # WIN - fill month/day by yourself :P
# or use this ugly, but working batch script
#for /f "tokens=2 delims==" %%i in ('wmic os get localdatetime /value') do set "dt=%%i"
#set "month=%dt:~4,2%"
#set "day=%dt:~6,2%"
#set "pattern=%month%-%day%"
#exiftool -r -if "$DateTimeOriginal =~ /%pattern%/" -p "${FilePath;s/\//\\/g}" d:\_foto\.
# LIN: process only certain extensions. Looks simple, but in Linux *.cr2 would ommit IMG_1337.CR2 (i.e. upper case)
exiftool {$COMMANDS} -ext cr2 .
# Output full, absolute path with filename
exiftool {$COMMANDS} -p "${FilePath;s/\//\\/g}" . # WIN (changes / to \)
exiftool {$COMMANDS} -p "${FilePath}" . # LIN
# Get full (including namespace!) attribute name (useful for setting/getting them, but also for conditions [!] - ctrl+f GETTAG)
# Example output:
# -XMP:Rating=0
# -MakerNotes:MacroMode=Normal
# Now we know that to change rating, we should use -XMP:Rating
exiftool -args -G0 .
# Offset date.
# ${PATTERN} is e.g. "12:0:6 1:3:0"- it means to change year to 12 years later, keep month, 6 days later,
# 1 hour later, 3 minutes later, keep seconds. OFC it's yy:mm:dd hh:mm:ss
# ${OFFSET} is + or -
# Alldates is for all dates (duh!) but you may want to use just some of them, like FileModifyDate or DateTimeOriginal
# So the command to change exif date of all hours by +2 would be:
# exiftool "-DateTimeOriginal+=0:0:0 2:0:0"
exiftool "-Alldates{$OFFSET}={$PATTERN}" .
# Write multiline description
exiftool -ec -description="foo\nbar" .
# Compute hash of image DATA (meaning that files with different metadata will yield the same hash)
# That's wonderful for finding duplicates, which most duplicate finders won't see (as file hash differs)
# The following command will output something like:
# somepath/IMG_1285.CR2|cad6c851659a4d5828da10655c7987c9
# somepath/IMG_1285_NO_METADATA.CR2|cad6c851659a4d5828da10655c7987c9
# which then might be fed to script checking for duplicate values
exiftool -p "$filepath|$imagedatamd5" .
# Extract big raw preview (usually embedded JPEG) to file, where filename matches original and extension is set
# to the proper format of preview. E.g. IMG_1337.CR2 will be extracted to IMG_1337.jpg
exiftool -b -PreviewImage -W "%f.%s" -ext cr2 .
# Changing date format is useful not just for display, but also for processing and conditions.
# Here: move files to YYYY-MM directories
exiftool -d "%%Y-%%m" "-directory<DateTimeOriginal" .
# Rotate JPEG preview in raw. Because changing orientation in metadata won't fix preview that
# most viewers will show.
# Requires JPEGTRAN (official download: https://jpegclub.org/jpegtran/)
# WARNING: Use with caution. For some reason some thumbnailers don't like rotated jpegs
# -overwrite_original is DISCOURAGED
# {$ROTATE} is:
# 90 - Clockwise
# 270 - Counterclockwise
# 180 - Flip
# So e.g. to rotate CW, use jpegtran -rotate {$ROTATE} -copy all -outfile {$PATHTEMP} {$PATHIN}
# {$PATHIN} is just name of your input file
# {$PATHTEMP-ORIG} and {$PATHTEMP-ROTATED} are two different paths to temporary files.
# For Windows you may use %TEMP%\preview.jpg and %TEMP%\preview-rotated.jpg
# For Bash (e.g. Linux) use "tmp_file=$(mktemp --suffix=.jpg)" and then change {$PATHTEMP-ORIG} to $tmp_file
# (two times, two vars, for two different files)
exiftool -PreviewImage -b {$PATHIN} > {$PATHTEMP-ORIG}
jpegtran -rotate {$ROTATE} -copy all -outfile {$PATHTEMP-ROTATED} {$PATHTEMP-ORIG}
exiftool "-PreviewImage<={$PATHTEMP-ROTATED}" {$PATHIN}
del {$PATHTEMP} # WIN
rm {$PATHTEMP} # LIN
# ==========
# CONDITIONS
# ==========
# Use as part of the command, e.g.
# exiftool -if "lc($Software) =~ /inkscape/" -title="Inkscape file!" .
# Condition: image size (in pixels). OFC you can change < to >
-if "$imagesize and ($imagewidth<{$WIDTH} or $imageheight<{$HEIGHT})"
# Case insensitive condition - look for {$string} (must be lowercase)!
# So, "lc($Software) =~ /inkscape/" fits Inkscape, inkscape, iNkScApE etc.
# As =~ looks for string inside string, it will fit "Inkscape 5" or "This is Inkscape"
-if "lc($Software) =~ /{$string}/"
# Boolean conditions
-if "($SignificantBits eq '8 8 8 8') || (lc($Software) =~ /inkscape/)" # OR
-if "($SignificantBits eq '8 8 8 8') && (lc($Software) =~ /inkscape/)" # AND
# Check if a specific tag contains given string (ctrl+f GETTAG)
-if "$EXIF:Model =~ /(Samsung)/"
# Check tag count (warning: I noticed some tags have ";" or "|" separator, here it's for ",")
# {$COUNT} is number you want to check against
# {$OPERATOR} is comparison operator
# For e.g. to check if there's single tag in $RegionPersonDisplayName (ctrl+f GETTAG):
# -if 'scalar(split(/,/, $RegionPersonDisplayName)) == 1'
-if "scalar(split(/,/, $RegionPersonDisplayName)) {$OPERATOR} {$COUNT}"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment