How to Autorotate JPEG Images: Tools & Best Practices

Lightweight JPEG Autorotate Scripts for Windows, macOS, and Linux

Photos taken on phones and cameras sometimes open sideways or upside down because the image’s EXIF orientation flag doesn’t match how programs display it. Autorotating JPEGs based on EXIF orientation fixes this without re-editing every image manually. Below are simple, lightweight scripts for Windows (PowerShell), macOS (shell with sips), and Linux (shell with exiftool and jpegtran) to autorotate JPEGs in place or to an output folder. Each script is fast, minimal, and suitable for batch processing.

How this works (brief)

  • JPEG files can store an EXIF Orientation tag (1–8) that tells viewers how to rotate/flip the image for correct display.
  • Autorotate scripts read the EXIF tag, apply the necessary rotation/flip to the actual pixels, then clear or set the orientation tag to “normal” so all viewers see the correct orientation.
  • These scripts aim to be dependency-light and safe: they can process recursively, skip non-JPEGs, and optionally back up files.

Cross-platform considerations

  • Always back up originals before bulk changes. Scripts include an optional backup step.
  • Some tools modify files in place; others produce new files. Adjust behavior depending on your workflow.
  • Test scripts on a small sample folder first.

1) Windows — PowerShell (no external install)

This PowerShell script uses .NET’s System.Drawing.Image to read EXIF orientation and rotate the image. It works on Windows ⁄11 with PowerShell 5+.

Usage: Save as Autorotate-JPEG.ps1 and run:

  • To autorotate in place: .\Autorotate-JPEG.ps1 -Path “C:\Photos” -Recursive
  • To backup originals: add -Backup

powershell

param( [string]\(Path</span><span> = </span><span class="token" style="color: rgb(163, 21, 21);">"."</span><span class="token" style="color: rgb(57, 58, 52);">,</span><span> </span><span> </span><span class="token">[switch]</span><span class="token" style="color: rgb(54, 172, 170);">\)Recursive = \(false</span><span class="token" style="color: rgb(57, 58, 52);">,</span><span> </span><span> </span><span class="token">[switch]</span><span class="token" style="color: rgb(54, 172, 170);">\)Backup = \(false</span><span> </span><span></span><span class="token" style="color: rgb(57, 58, 52);">)</span><span> </span> <span></span><span class="token" style="color: rgb(57, 58, 52);">Add-Type</span><span> </span><span class="token" style="color: rgb(57, 58, 52);">-</span><span>AssemblyName System</span><span class="token" style="color: rgb(57, 58, 52);">.</span><span>Drawing </span> <span></span><span class="token" style="color: rgb(54, 172, 170);">\)files = Get-ChildItem -Path \(Path</span><span> </span><span class="token" style="color: rgb(57, 58, 52);">-</span><span>Include </span><span class="token" style="color: rgb(57, 58, 52);">*</span><span class="token" style="color: rgb(57, 58, 52);">.</span><span>jpg</span><span class="token" style="color: rgb(57, 58, 52);">,</span><span class="token" style="color: rgb(57, 58, 52);">*</span><span class="token" style="color: rgb(57, 58, 52);">.</span><span>jpeg </span><span class="token" style="color: rgb(57, 58, 52);">-</span><span>File </span><span class="token" style="color: rgb(57, 58, 52);">-</span><span>ErrorAction SilentlyContinue </span><span></span><span class="token" style="color: rgb(0, 0, 255);">if</span><span> </span><span class="token" style="color: rgb(57, 58, 52);">(</span><span class="token" style="color: rgb(54, 172, 170);">\)Recursive) { \(files</span><span> = </span><span class="token" style="color: rgb(57, 58, 52);">Get-ChildItem</span><span> </span><span class="token" style="color: rgb(57, 58, 52);">-</span><span>Path </span><span class="token" style="color: rgb(54, 172, 170);">\)Path -Include .jpg,.jpeg -File -Recurse -ErrorAction SilentlyContinue } foreach (\(f</span><span> in </span><span class="token" style="color: rgb(54, 172, 170);">\)files) { try { \(img</span><span> = </span><span class="token">[System.Drawing.Image]</span><span>::FromFile</span><span class="token" style="color: rgb(57, 58, 52);">(</span><span class="token" style="color: rgb(54, 172, 170);">\)f.FullName) \(propId</span><span> = 0x0112 </span><span> </span><span class="token" style="color: rgb(0, 0, 255);">if</span><span> </span><span class="token" style="color: rgb(57, 58, 52);">(</span><span class="token" style="color: rgb(54, 172, 170);">\)img.PropertyIdList -contains \(propId</span><span class="token" style="color: rgb(57, 58, 52);">)</span><span> </span><span class="token" style="color: rgb(57, 58, 52);">{</span><span> </span><span> </span><span class="token" style="color: rgb(54, 172, 170);">\)orientProp = \(img</span><span class="token" style="color: rgb(57, 58, 52);">.</span><span>GetPropertyItem</span><span class="token" style="color: rgb(57, 58, 52);">(</span><span class="token" style="color: rgb(54, 172, 170);">\)propId) \(orientation</span><span> = </span><span class="token">[BitConverter]</span><span>::ToUInt16</span><span class="token" style="color: rgb(57, 58, 52);">(</span><span class="token" style="color: rgb(54, 172, 170);">\)orientProp.Value,0) switch (\(orientation</span><span class="token" style="color: rgb(57, 58, 52);">)</span><span> </span><span class="token" style="color: rgb(57, 58, 52);">{</span><span> </span><span> 1 </span><span class="token" style="color: rgb(57, 58, 52);">{</span><span> </span><span class="token" style="color: rgb(54, 172, 170);">\)rotated = \(false</span><span> </span><span class="token" style="color: rgb(57, 58, 52);">}</span><span> </span><span> 2 </span><span class="token" style="color: rgb(57, 58, 52);">{</span><span> </span><span class="token" style="color: rgb(54, 172, 170);">\)img.RotateFlip([System.Drawing.RotateFlipType]::RotateNoneFlipX); \(rotated</span><span> = </span><span class="token" style="color: rgb(54, 172, 170);">\)true } 3 { \(img</span><span class="token" style="color: rgb(57, 58, 52);">.</span><span>RotateFlip</span><span class="token" style="color: rgb(57, 58, 52);">(</span><span class="token">[System.Drawing.RotateFlipType]</span><span>::Rotate180FlipNone</span><span class="token" style="color: rgb(57, 58, 52);">)</span><span class="token" style="color: rgb(57, 58, 52);">;</span><span> </span><span class="token" style="color: rgb(54, 172, 170);">\)rotated = \(true</span><span> </span><span class="token" style="color: rgb(57, 58, 52);">}</span><span> </span><span> 4 </span><span class="token" style="color: rgb(57, 58, 52);">{</span><span> </span><span class="token" style="color: rgb(54, 172, 170);">\)img.RotateFlip([System.Drawing.RotateFlipType]::Rotate180FlipX); \(rotated</span><span> = </span><span class="token" style="color: rgb(54, 172, 170);">\)true } 5 { \(img</span><span class="token" style="color: rgb(57, 58, 52);">.</span><span>RotateFlip</span><span class="token" style="color: rgb(57, 58, 52);">(</span><span class="token">[System.Drawing.RotateFlipType]</span><span>::Rotate90FlipX</span><span class="token" style="color: rgb(57, 58, 52);">)</span><span class="token" style="color: rgb(57, 58, 52);">;</span><span> </span><span class="token" style="color: rgb(54, 172, 170);">\)rotated = \(true</span><span> </span><span class="token" style="color: rgb(57, 58, 52);">}</span><span> </span><span> 6 </span><span class="token" style="color: rgb(57, 58, 52);">{</span><span> </span><span class="token" style="color: rgb(54, 172, 170);">\)img.RotateFlip([System.Drawing.RotateFlipType]::Rotate90FlipNone); \(rotated</span><span> = </span><span class="token" style="color: rgb(54, 172, 170);">\)true } 7 { \(img</span><span class="token" style="color: rgb(57, 58, 52);">.</span><span>RotateFlip</span><span class="token" style="color: rgb(57, 58, 52);">(</span><span class="token">[System.Drawing.RotateFlipType]</span><span>::Rotate270FlipX</span><span class="token" style="color: rgb(57, 58, 52);">)</span><span class="token" style="color: rgb(57, 58, 52);">;</span><span> </span><span class="token" style="color: rgb(54, 172, 170);">\)rotated = \(true</span><span> </span><span class="token" style="color: rgb(57, 58, 52);">}</span><span> </span><span> 8 </span><span class="token" style="color: rgb(57, 58, 52);">{</span><span> </span><span class="token" style="color: rgb(54, 172, 170);">\)img.RotateFlip([System.Drawing.RotateFlipType]::Rotate270FlipNone); \(rotated</span><span> = </span><span class="token" style="color: rgb(54, 172, 170);">\)true } default { \(rotated</span><span> = </span><span class="token" style="color: rgb(54, 172, 170);">\)false } } if (\(rotated</span><span class="token" style="color: rgb(57, 58, 52);">)</span><span> </span><span class="token" style="color: rgb(57, 58, 52);">{</span><span> </span><span> </span><span class="token" style="color: rgb(0, 0, 255);">if</span><span> </span><span class="token" style="color: rgb(57, 58, 52);">(</span><span class="token" style="color: rgb(54, 172, 170);">\)Backup) { Copy-Item -Path \(f</span><span class="token" style="color: rgb(57, 58, 52);">.</span><span>FullName </span><span class="token" style="color: rgb(57, 58, 52);">-</span><span>Destination </span><span class="token" style="color: rgb(57, 58, 52);">(</span><span class="token" style="color: rgb(54, 172, 170);">\)f.FullName + ”.bak”) -Force } # set orientation to 1 \(orientProp</span><span class="token" style="color: rgb(57, 58, 52);">.</span><span>Value = </span><span class="token">[byte[]]</span><span> </span><span class="token" style="color: rgb(57, 58, 52);">(</span><span>1</span><span class="token" style="color: rgb(57, 58, 52);">,</span><span>0</span><span class="token" style="color: rgb(57, 58, 52);">)</span><span> </span><span> </span><span class="token" style="color: rgb(54, 172, 170);">\)img.SetPropertyItem(\(orientProp</span><span class="token" style="color: rgb(57, 58, 52);">)</span><span> </span><span> </span><span class="token" style="color: rgb(54, 172, 170);">\)img.Save(\(f</span><span class="token" style="color: rgb(57, 58, 52);">.</span><span>FullName</span><span class="token" style="color: rgb(57, 58, 52);">,</span><span> </span><span class="token">[System.Drawing.Imaging.ImageFormat]</span><span>::Jpeg</span><span class="token" style="color: rgb(57, 58, 52);">)</span><span> </span><span> </span><span class="token" style="color: rgb(57, 58, 52);">Write-Output</span><span> </span><span class="token" style="color: rgb(163, 21, 21);">"Rotated: </span><span class="token" style="color: rgb(57, 58, 52);">\)(\(f</span><span class="token" style="color: rgb(57, 58, 52);">.</span><span class="token" style="color: rgb(57, 58, 52);">FullName</span><span class="token" style="color: rgb(57, 58, 52);">)</span><span class="token" style="color: rgb(163, 21, 21);"> -> orientation </span><span class="token" style="color: rgb(54, 172, 170);">\)orientation } } \(img</span><span class="token" style="color: rgb(57, 58, 52);">.</span><span>Dispose</span><span class="token" style="color: rgb(57, 58, 52);">(</span><span class="token" style="color: rgb(57, 58, 52);">)</span><span> </span><span> </span><span class="token" style="color: rgb(57, 58, 52);">}</span><span> </span><span class="token" style="color: rgb(0, 0, 255);">catch</span><span> </span><span class="token" style="color: rgb(57, 58, 52);">{</span><span> </span><span> </span><span class="token" style="color: rgb(57, 58, 52);">Write-Warning</span><span> </span><span class="token" style="color: rgb(163, 21, 21);">"Failed: </span><span class="token" style="color: rgb(57, 58, 52);">\)(\(f</span><span class="token" style="color: rgb(57, 58, 52);">.</span><span class="token" style="color: rgb(57, 58, 52);">FullName</span><span class="token" style="color: rgb(57, 58, 52);">)</span><span class="token" style="color: rgb(163, 21, 21);"> - </span><span class="token" style="color: rgb(57, 58, 52);">\)(\(_</span><span class="token" style="color: rgb(57, 58, 52);">.</span><span class="token" style="color: rgb(57, 58, 52);">Exception</span><span class="token" style="color: rgb(57, 58, 52);">.</span><span class="token" style="color: rgb(57, 58, 52);">Message</span><span class="token" style="color: rgb(57, 58, 52);">)</span><span class="token" style="color: rgb(163, 21, 21);">"</span><span> </span><span> </span><span class="token" style="color: rgb(57, 58, 52);">}</span><span> </span><span></span><span class="token" style="color: rgb(57, 58, 52);">}</span><span> </span></code></div></div></pre> <p>Notes:</p> <ul> <li>System.Drawing is available by default on Windows PowerShell. If using PowerShell Core on non-Windows, System.Drawing may behave differently.</li> <li>This rewrites JPEGs and sets the EXIF orientation to normal.</li> </ul> <h2>2) macOS — shell script using sips (built-in)</h2> <p>macOS includes sips, which can read and rotate images using EXIF orientation. This script rotates images into place and optionally copies originals to a backup folder.</p> <p>Usage: Save as autorotate-macos.sh and run:</p> <ul> <li>In-place: ./autorotate-macos.sh /Users/you/Pictures</li> <li>With backup: ./autorotate-macos.sh /Users/you/Pictures backup</li> </ul> <pre><div class="XG2rBS5V967VhGTCEN1k"><div class="nHykNMmtaaTJMjgzStID"><div class="HsT0RHFbNELC00WicOi8"><i><svg width="16" height="16" fill="none" xmlns="http://www.w3.org/2000/svg"><path fill="currentColor" fill-rule="evenodd" clip-rule="evenodd" d="M15.434 7.51c.137.137.212.311.212.49a.694.694 0 0 1-.212.5l-3.54 3.5a.893.893 0 0 1-.277.18 1.024 1.024 0 0 1-.684.038.945.945 0 0 1-.302-.148.787.787 0 0 1-.213-.234.652.652 0 0 1-.045-.58.74.74 0 0 1 .175-.256l3.045-3-3.045-3a.69.69 0 0 1-.22-.55.723.723 0 0 1 .303-.52 1 1 0 0 1 .648-.186.962.962 0 0 1 .614.256l3.541 3.51Zm-12.281 0A.695.695 0 0 0 2.94 8a.694.694 0 0 0 .213.5l3.54 3.5a.893.893 0 0 0 .277.18 1.024 1.024 0 0 0 .684.038.945.945 0 0 0 .302-.148.788.788 0 0 0 .213-.234.651.651 0 0 0 .045-.58.74.74 0 0 0-.175-.256L4.994 8l3.045-3a.69.69 0 0 0 .22-.55.723.723 0 0 0-.303-.52 1 1 0 0 0-.648-.186.962.962 0 0 0-.615.256l-3.54 3.51Z"></path></svg></i><p class="li3asHIMe05JPmtJCytG wZ4JdaHxSAhGy1HoNVja cPy9QU4brI7VQXFNPEvF">bash</p></div><div class="CF2lgtGWtYUYmTULoX44"><button type="button" class="st68fcLUUT0dNcuLLB2_ ffON2NH02oMAcqyoh2UU MQCbz04ET5EljRmK3YpQ CPXAhl7VTkj2dHDyAYAf" data-copycode="true" role="button" aria-label="Copy Code"><svg viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg"><path fill="currentColor" fill-rule="evenodd" clip-rule="evenodd" d="M9.975 1h.09a3.2 3.2 0 0 1 3.202 3.201v1.924a.754.754 0 0 1-.017.16l1.23 1.353A2 2 0 0 1 15 8.983V14a2 2 0 0 1-2 2H8a2 2 0 0 1-1.733-1H4.183a3.201 3.201 0 0 1-3.2-3.201V4.201a3.2 3.2 0 0 1 3.04-3.197A1.25 1.25 0 0 1 5.25 0h3.5c.604 0 1.109.43 1.225 1ZM4.249 2.5h-.066a1.7 1.7 0 0 0-1.7 1.701v7.598c0 .94.761 1.701 1.7 1.701H6V7a2 2 0 0 1 2-2h3.197c.195 0 .387.028.57.083v-.882A1.7 1.7 0 0 0 10.066 2.5H9.75c-.228.304-.591.5-1 .5h-3.5c-.41 0-.772-.196-1-.5ZM5 1.75v-.5A.25.25 0 0 1 5.25 1h3.5a.25.25 0 0 1 .25.25v.5a.25.25 0 0 1-.25.25h-3.5A.25.25 0 0 1 5 1.75ZM7.5 7a.5.5 0 0 1 .5-.5h3V9a1 1 0 0 0 1 1h1.5v4a.5.5 0 0 1-.5.5H8a.5.5 0 0 1-.5-.5V7Zm6 2v-.017a.5.5 0 0 0-.13-.336L12 7.14V9h1.5Z"></path></svg>Copy Code</button><button type="button" class="st68fcLUUT0dNcuLLB2_ WtfzoAXPoZC2mMqcexgL ffON2NH02oMAcqyoh2UU MQCbz04ET5EljRmK3YpQ GnLX_jUB3Jn3idluie7R"><svg fill="none" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path fill="currentColor" fill-rule="evenodd" d="M20.618 4.214a1 1 0 0 1 .168 1.404l-11 14a1 1 0 0 1-1.554.022l-5-6a1 1 0 0 1 1.536-1.28l4.21 5.05L19.213 4.382a1 1 0 0 1 1.404-.168Z" clip-rule="evenodd"></path></svg>Copied</button></div></div><div class="mtDfw7oSa1WexjXyzs9y" style="color: var(--sds-color-text-01); font-family: var(--sds-font-family-monospace); direction: ltr; text-align: left; white-space: pre; word-spacing: normal; word-break: normal; font-size: var(--sds-font-size-label); line-height: 1.2em; tab-size: 4; hyphens: none; padding: var(--sds-space-x02, 8px) var(--sds-space-x04, 16px) var(--sds-space-x04, 16px); margin: 0px; overflow: auto; border: none; background: transparent;"><code class="language-bash" style="color: rgb(57, 58, 52); font-family: Consolas, "Bitstream Vera Sans Mono", "Courier New", Courier, monospace; direction: ltr; text-align: left; white-space: pre; word-spacing: normal; word-break: normal; font-size: 0.9em; line-height: 1.2em; tab-size: 4; hyphens: none;"><span class="token shebang" style="color: rgb(238, 153, 0); font-weight: bold;">#!/bin/bash</span><span> </span><span></span><span class="token assign-left" style="color: rgb(54, 172, 170);">DIR</span><span class="token" style="color: rgb(57, 58, 52);">=</span><span class="token" style="color: rgb(163, 21, 21);">"</span><span class="token" style="color: rgb(54, 172, 170);">\){1:-.} BACKUP=\({2</span><span class="token" style="color: rgb(57, 58, 52);">:-</span><span class="token" style="color: rgb(54, 172, 170);">}</span><span class="token" style="color: rgb(163, 21, 21);">"</span><span> </span><span></span><span class="token builtin" style="color: rgb(43, 145, 175);">shopt</span><span> -s nullglob </span> <span></span><span class="token" style="color: rgb(0, 0, 255);">if</span><span> </span><span class="token" style="color: rgb(57, 58, 52);">[</span><span> -n </span><span class="token" style="color: rgb(163, 21, 21);">"</span><span class="token" style="color: rgb(54, 172, 170);">\)BACKUP ]; then mkdir -p \(DIR</span><span class="token" style="color: rgb(163, 21, 21);">/backup_autorotate"</span><span> </span><span></span><span class="token" style="color: rgb(0, 0, 255);">fi</span><span> </span> <span></span><span class="token" style="color: rgb(57, 58, 52);">find</span><span> </span><span class="token" style="color: rgb(163, 21, 21);">"</span><span class="token" style="color: rgb(54, 172, 170);">\)DIR -type f </span>( -iname .jpg” -o -iname .jpeg” </span>) | while read -r file; do # read orientation ori=\((</span><span class="token" style="color: rgb(54, 172, 170);">sips -g orientation </span><span class="token" style="color: rgb(163, 21, 21);">"</span><span class="token" style="color: rgb(163, 21, 21);">\)file 2>/dev/null | awk ’/orientation:/ {print \(2}'</span><span class="token" style="color: rgb(54, 172, 170);">)</span><span> </span><span> </span><span class="token" style="color: rgb(0, 0, 255);">if</span><span> </span><span class="token" style="color: rgb(57, 58, 52);">[</span><span> -z </span><span class="token" style="color: rgb(163, 21, 21);">"</span><span class="token" style="color: rgb(54, 172, 170);">\)ori ] || [ \(ori</span><span class="token" style="color: rgb(163, 21, 21);">"</span><span> </span><span class="token" style="color: rgb(57, 58, 52);">=</span><span> </span><span class="token" style="color: rgb(163, 21, 21);">"1"</span><span> </span><span class="token" style="color: rgb(57, 58, 52);">]</span><span class="token" style="color: rgb(57, 58, 52);">;</span><span> </span><span class="token" style="color: rgb(0, 0, 255);">then</span><span> </span><span> </span><span class="token builtin" style="color: rgb(43, 145, 175);">continue</span><span> </span><span> </span><span class="token" style="color: rgb(0, 0, 255);">fi</span><span> </span> <span> </span><span class="token" style="color: rgb(0, 0, 255);">if</span><span> </span><span class="token" style="color: rgb(57, 58, 52);">[</span><span> -n </span><span class="token" style="color: rgb(163, 21, 21);">"</span><span class="token" style="color: rgb(54, 172, 170);">\)BACKUP ]; then cp\(file</span><span class="token" style="color: rgb(163, 21, 21);">"</span><span> </span><span class="token" style="color: rgb(163, 21, 21);">"</span><span class="token" style="color: rgb(54, 172, 170);">\)DIR/backupautorotate/” fi case \(ori</span><span class="token" style="color: rgb(163, 21, 21);">"</span><span> </span><span class="token" style="color: rgb(0, 0, 255);">in</span><span> </span><span> </span><span class="token" style="color: rgb(54, 172, 170);">1</span><span class="token" style="color: rgb(57, 58, 52);">)</span><span> </span><span class="token" style="color: rgb(57, 58, 52);">;</span><span class="token" style="color: rgb(57, 58, 52);">;</span><span> </span><span class="token" style="color: rgb(0, 128, 0); font-style: italic;"># normal</span><span> </span><span> </span><span class="token" style="color: rgb(54, 172, 170);">2</span><span class="token" style="color: rgb(57, 58, 52);">)</span><span> sips --flipHorizontal </span><span class="token" style="color: rgb(163, 21, 21);">"</span><span class="token" style="color: rgb(54, 172, 170);">\)file >/dev/null ;; 3) sips –rotate 180 \(file</span><span class="token" style="color: rgb(163, 21, 21);">"</span><span> </span><span class="token" style="color: rgb(57, 58, 52);">></span><span>/dev/null </span><span class="token" style="color: rgb(57, 58, 52);">;</span><span class="token" style="color: rgb(57, 58, 52);">;</span><span> </span><span> </span><span class="token" style="color: rgb(54, 172, 170);">4</span><span class="token" style="color: rgb(57, 58, 52);">)</span><span> sips --flipVertical </span><span class="token" style="color: rgb(163, 21, 21);">"</span><span class="token" style="color: rgb(54, 172, 170);">\)file >/dev/null ;; 5) sips –rotate 90 –flipHorizontal \(file</span><span class="token" style="color: rgb(163, 21, 21);">"</span><span> </span><span class="token" style="color: rgb(57, 58, 52);">></span><span>/dev/null </span><span class="token" style="color: rgb(57, 58, 52);">;</span><span class="token" style="color: rgb(57, 58, 52);">;</span><span> </span><span> </span><span class="token" style="color: rgb(54, 172, 170);">6</span><span class="token" style="color: rgb(57, 58, 52);">)</span><span> sips --rotate </span><span class="token" style="color: rgb(54, 172, 170);">90</span><span> </span><span class="token" style="color: rgb(163, 21, 21);">"</span><span class="token" style="color: rgb(54, 172, 170);">\)file >/dev/null ;; 7) sips –rotate 270 –flipHorizontal \(file</span><span class="token" style="color: rgb(163, 21, 21);">"</span><span> </span><span class="token" style="color: rgb(57, 58, 52);">></span><span>/dev/null </span><span class="token" style="color: rgb(57, 58, 52);">;</span><span class="token" style="color: rgb(57, 58, 52);">;</span><span> </span><span> </span><span class="token" style="color: rgb(54, 172, 170);">8</span><span class="token" style="color: rgb(57, 58, 52);">)</span><span> sips --rotate </span><span class="token" style="color: rgb(54, 172, 170);">270</span><span> </span><span class="token" style="color: rgb(163, 21, 21);">"</span><span class="token" style="color: rgb(54, 172, 170);">\)file >/dev/null ;; esac # clear orientation by rewriting metadata to normal — sips preserves orientation after pixel change echo “Rotated: \(file</span><span class="token" style="color: rgb(163, 21, 21);"> (orientation </span><span class="token" style="color: rgb(54, 172, 170);">\)ori)” done

Notes:

  • sips is fast and present on macOS. It adjusts pixels directly so images display correctly everywhere.
  • The script uses a simple backup folder if a second argument is provided.

3) Linux — shell script using exiftool + jpegtran (recommended) or mogrify

For Linux, exiftool is great for reading/clearing EXIF orientation and jpegtran or jpegoptim for lossless rotation. If you prefer a simple ImageMagick solution, a mogrify example follows.

Prerequisites (install once):

  • exiftool and jpegtran (from libjpeg-turbo) or ImageMagick (mogrify).
    • Debian/Ubuntu: sudo apt install libimage-exiftool-perl libjpeg-turbo-progs
    • Or: sudo apt install imagemagick

Option A — lossless rotations with exiftool + jpegtran (recommended):

Usage: Save as autorotate-linux.sh and run:

  • ./autorotate-linux.sh /path/to/photos

bash

#!/bin/bash DIR=\({1</span><span class="token" style="color: rgb(57, 58, 52);">:-</span><span class="token" style="color: rgb(54, 172, 170);">.}</span><span class="token" style="color: rgb(163, 21, 21);">"</span><span> </span><span></span><span class="token assign-left" style="color: rgb(54, 172, 170);">BACKUP_DIR</span><span class="token" style="color: rgb(57, 58, 52);">=</span><span class="token" style="color: rgb(163, 21, 21);">"</span><span class="token" style="color: rgb(54, 172, 170);">\){DIR}/backup_autorotate” mkdir -p \(BACKUP_DIR</span><span class="token" style="color: rgb(163, 21, 21);">"</span><span> </span> <span></span><span class="token" style="color: rgb(57, 58, 52);">find</span><span> </span><span class="token" style="color: rgb(163, 21, 21);">"</span><span class="token" style="color: rgb(54, 172, 170);">\)DIR -type f </span>( -iname .jpg” -o -iname .jpeg” </span>) | while read -r file; do ori=\((</span><span class="token" style="color: rgb(54, 172, 170);">exiftool -s -s -s -Orientation </span><span class="token" style="color: rgb(163, 21, 21);">"</span><span class="token" style="color: rgb(163, 21, 21);">\)file) if [ -z \(ori</span><span class="token" style="color: rgb(163, 21, 21);">"</span><span> </span><span class="token" style="color: rgb(57, 58, 52);">]</span><span> </span><span class="token" style="color: rgb(57, 58, 52);">||</span><span> </span><span class="token" style="color: rgb(57, 58, 52);">[</span><span> </span><span class="token" style="color: rgb(163, 21, 21);">"</span><span class="token" style="color: rgb(54, 172, 170);">\)ori = “Horizontal (normal)” ]; then continue fi cp\(file</span><span class="token" style="color: rgb(163, 21, 21);">"</span><span> </span><span class="token" style="color: rgb(163, 21, 21);">"</span><span class="token" style="color: rgb(54, 172, 170);">\)BACKUP_DIR/” case \(ori</span><span class="token" style="color: rgb(163, 21, 21);">"</span><span> </span><span class="token" style="color: rgb(0, 0, 255);">in</span><span> </span><span> </span><span class="token" style="color: rgb(163, 21, 21);">"Rotate 90 CW"</span><span class="token" style="color: rgb(57, 58, 52);">)</span><span> </span><span> jpegtran -copy all -rotate </span><span class="token" style="color: rgb(54, 172, 170);">90</span><span> -outfile </span><span class="token" style="color: rgb(163, 21, 21);">"</span><span class="token" style="color: rgb(54, 172, 170);">\){file}.tmp” \(file</span><span class="token" style="color: rgb(163, 21, 21);">"</span><span> </span><span class="token" style="color: rgb(57, 58, 52);">&&</span><span> </span><span class="token" style="color: rgb(57, 58, 52);">mv</span><span> </span><span class="token" style="color: rgb(163, 21, 21);">"</span><span class="token" style="color: rgb(54, 172, 170);">\){file}.tmp” \(file</span><span class="token" style="color: rgb(163, 21, 21);">"</span><span> </span><span class="token" style="color: rgb(57, 58, 52);">;</span><span class="token" style="color: rgb(57, 58, 52);">;</span><span> </span><span> </span><span class="token" style="color: rgb(163, 21, 21);">"Rotate 180"</span><span class="token" style="color: rgb(57, 58, 52);">)</span><span> </span><span> jpegtran -copy all -rotate </span><span class="token" style="color: rgb(54, 172, 170);">180</span><span> -outfile </span><span class="token" style="color: rgb(163, 21, 21);">"</span><span class="token" style="color: rgb(54, 172, 170);">\){file}.tmp” \(file</span><span class="token" style="color: rgb(163, 21, 21);">"</span><span> </span><span class="token" style="color: rgb(57, 58, 52);">&&</span><span> </span><span class="token" style="color: rgb(57, 58, 52);">mv</span><span> </span><span class="token" style="color: rgb(163, 21, 21);">"</span><span class="token" style="color: rgb(54, 172, 170);">\){file}.tmp” \(file</span><span class="token" style="color: rgb(163, 21, 21);">"</span><span> </span><span class="token" style="color: rgb(57, 58, 52);">;</span><span class="token" style="color: rgb(57, 58, 52);">;</span><span> </span><span> </span><span class="token" style="color: rgb(163, 21, 21);">"Rotate 270 CW"</span><span class="token" style="color: rgb(57, 58, 52);">)</span><span> </span><span> jpegtran -copy all -rotate </span><span class="token" style="color: rgb(54, 172, 170);">270</span><span> -outfile </span><span class="token" style="color: rgb(163, 21, 21);">"</span><span class="token" style="color: rgb(54, 172, 170);">\){file}.tmp” \(file</span><span class="token" style="color: rgb(163, 21, 21);">"</span><span> </span><span class="token" style="color: rgb(57, 58, 52);">&&</span><span> </span><span class="token" style="color: rgb(57, 58, 52);">mv</span><span> </span><span class="token" style="color: rgb(163, 21, 21);">"</span><span class="token" style="color: rgb(54, 172, 170);">\){file}.tmp” \(file</span><span class="token" style="color: rgb(163, 21, 21);">"</span><span> </span><span class="token" style="color: rgb(57, 58, 52);">;</span><span class="token" style="color: rgb(57, 58, 52);">;</span><span> </span><span> </span><span class="token" style="color: rgb(163, 21, 21);">"Mirror horizontal"</span><span class="token" style="color: rgb(57, 58, 52);">)</span><span> </span><span class="token" style="color: rgb(0, 128, 0); font-style: italic;"># rare, try jpegtran flip</span><span> </span><span> jpegtran -copy all -flip horizontal -outfile </span><span class="token" style="color: rgb(163, 21, 21);">"</span><span class="token" style="color: rgb(54, 172, 170);">\){file}.tmp” \(file</span><span class="token" style="color: rgb(163, 21, 21);">"</span><span> </span><span class="token" style="color: rgb(57, 58, 52);">&&</span><span> </span><span class="token" style="color: rgb(57, 58, 52);">mv</span><span> </span><span class="token" style="color: rgb(163, 21, 21);">"</span><span class="token" style="color: rgb(54, 172, 170);">\){file}.tmp” \(file</span><span class="token" style="color: rgb(163, 21, 21);">"</span><span> </span><span class="token" style="color: rgb(57, 58, 52);">;</span><span class="token" style="color: rgb(57, 58, 52);">;</span><span> </span><span> </span><span class="token" style="color: rgb(163, 21, 21);">"Mirror vertical"</span><span class="token" style="color: rgb(57, 58, 52);">)</span><span> </span><span> jpegtran -copy all -flip vertical -outfile </span><span class="token" style="color: rgb(163, 21, 21);">"</span><span class="token" style="color: rgb(54, 172, 170);">\){file}.tmp” \(file</span><span class="token" style="color: rgb(163, 21, 21);">"</span><span> </span><span class="token" style="color: rgb(57, 58, 52);">&&</span><span> </span><span class="token" style="color: rgb(57, 58, 52);">mv</span><span> </span><span class="token" style="color: rgb(163, 21, 21);">"</span><span class="token" style="color: rgb(54, 172, 170);">\){file}.tmp” \(file</span><span class="token" style="color: rgb(163, 21, 21);">"</span><span> </span><span class="token" style="color: rgb(57, 58, 52);">;</span><span class="token" style="color: rgb(57, 58, 52);">;</span><span> </span><span> *</span><span class="token" style="color: rgb(57, 58, 52);">)</span><span> </span><span> </span><span class="token" style="color: rgb(0, 128, 0); font-style: italic;"># handle combined cases via ImageMagick fallback</span><span> </span><span> convert </span><span class="token" style="color: rgb(163, 21, 21);">"</span><span class="token" style="color: rgb(54, 172, 170);">\)file -auto-orient \(file</span><span class="token" style="color: rgb(163, 21, 21);">"</span><span> </span><span class="token" style="color: rgb(57, 58, 52);">;</span><span class="token" style="color: rgb(57, 58, 52);">;</span><span> </span><span> </span><span class="token" style="color: rgb(0, 0, 255);">esac</span><span> </span> <span> </span><span class="token" style="color: rgb(0, 128, 0); font-style: italic;"># clear orientation tag</span><span> </span><span> exiftool -overwrite_original -Orientation</span><span class="token" style="color: rgb(57, 58, 52);">=</span><span> </span><span class="token" style="color: rgb(163, 21, 21);">"</span><span class="token" style="color: rgb(54, 172, 170);">\)file >/dev/null echo “Rotated: \(file</span><span class="token" style="color: rgb(163, 21, 21);"> -> </span><span class="token" style="color: rgb(54, 172, 170);">\)ori done

Option B — ImageMagick (mogrify or convert) — simpler, may recompress:

  • Lossy re-encoding with ImageMagick: mogrify -auto-orient .jpg
  • To process recursively: find . -iname ‘.jpg’ -exec mogrify -auto-orient {} +

Notes:

  • jpegtran performs lossless rotation when JPEG MCU blocks align; exiftool clears the orientation tag afterward.
  • ImageMagick’s auto-orient is easy but re-encodes images which may slightly lower quality.

Safety and performance tips

  • Work on copies when first running these scripts to confirm behavior.
  • For very large batches, run on a subset to measure time and disk I/O.
  • Use parallel find/xargs or GNU parallel for speed if needed.
  • Keep backups for a short period; remove after confirming results.

Troubleshooting

  • If rotated images still appear incorrect in some viewers, clear EXIF orientation explicitly (exiftool -Orientation=) after rotation.
  • Very small or progressive JPEGs may behave differently with jpegtran; test first.
  • If System.Drawing fails on PowerShell Core (non-Windows), run the Windows script on Windows PowerShell or use ImageMagick.

Quick summary

  • Windows: PowerShell + System.Drawing for a no-install solution.
  • macOS: sips for a built-in, fast approach.
  • Linux: exiftool + jpegtran for lossless rotations; ImageMagick as a simple fallback.

If you want, I can:

  • Provide a single unified script that detects the OS and runs the appropriate commands, or
  • Customize any script to preserve metadata differently, run in dry-run mode, or process only specific subfolders.

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *