The fastest way to read what is inside an animated GIF (its dimensions, how many frames it has, how it loops, and the delay on each frame) is gifsicle --info:
gifsicle --info in.gifThat one command prints the logical screen size, the loop count, and a per-frame listing with each frame's position, size, transparency, and delay. If you just need the headline numbers, the rest of this page covers the targeted commands: counting frames with ImageMagick, deciding whether a GIF is animated at all, pulling out the first frame as a static image, and adding up the delays to get the total play time.
Both gifsicle and ImageMagick are one install away. On most systems it is sudo apt install gifsicle imagemagick (Debian/Ubuntu) or brew install gifsicle imagemagick (macOS). ImageMagick 7 ships the single magick entry point; the older standalone identify and convert binaries still work if your distro packages version 6, so where I use magick identify below, plain identify is the fallback.
Read everything at once with gifsicle
gifsicle -I (the short form of --info) is the most complete single view. For a small animation it prints something like this:
gifsicle --info in.gif* in.gif 12 images
logical screen 480x270
global color table [256]
loop forever
+ image #0 480x270 transparent 255
disposal asis delay 0.08s
+ image #1 480x270 transparent 255
disposal asis delay 0.08s
...
The first line gives the frame count (12 images). The logical screen line is the canvas size in pixels. loop forever is the loop count (a finite count shows as a number; see control a GIF's loop count for changing it). Each + image #N block is one frame, with its own size, offset, transparency index, disposal method, and delay in seconds. Frames in a GIF are often smaller than the canvas and positioned with an offset, which is why per-frame sizes can differ from the logical screen.
Count the frames
For a clean frame count, ImageMagick has two reliable approaches. The simplest is to let plain identify print one line per frame and count the lines:
magick identify in.gif | wc -lidentify in.gif lists every frame on its own line (in.gif[0] GIF ..., in.gif[1] GIF ..., and so on), so wc -l gives the frame count directly. The other approach uses the %n escape, but with a catch worth knowing:
magick identify -format "%n\n" in.gif | head -n 1%n is ImageMagick's "number of images in the sequence" escape (its frame count for a GIF), and ImageMagick evaluates the format string once per frame. A 12-frame GIF therefore prints 12 twelve times, so you take the first line with head -n 1 (or the last with tail -n 1). For a large GIF, add -ping to read only the header and skip decoding every frame: magick identify -ping -format "%n\n" in.gif | head -n 1.
Detect whether a GIF is animated
A GIF is animated when it has more than one frame. That makes the frame count the whole test. In a script:
frames=$(magick identify -format "%n\n" in.gif | head -n 1)
[ "$frames" -gt 1 ] && echo "animated ($frames frames)" || echo "static"This reads the frame count into a variable and branches on whether it exceeds 1. A single-frame .gif is just a static image with a GIF extension, and this returns static for it. Keep the head -n 1 in place: without it the %n repetition feeds a multi-line string into the numeric comparison and the test fails.
Extract the first frame as a static image
ImageMagick's bracket syntax selects a single frame by index, counting from zero. To save the first frame as a PNG:
magick "in.gif[0]" first.pngThe [0] reads only frame 0, so you get one static image rather than a multi-frame export. Quote the filename, because the brackets are shell glob characters and an unquoted in.gif[0] can be mangled by the shell before ImageMagick ever sees it. Any index works the same way: magick "in.gif[5]" frame5.png pulls the sixth frame. This is the standard trick for building a static "poster" image to show before a heavy GIF loads, then swapping in the real animation on interaction.
Total the play time from the delays
The total duration of an animated GIF is the sum of its per-frame delays. gifsicle --info already lists each delay in seconds; to total them without reading by eye, sum the centisecond delays ImageMagick reports per frame:
magick identify -format "%T\n" in.gif | awk '{ s += $1 } END { print s/100 " seconds" }'%T is the per-frame delay in centiseconds (hundredths of a second), the unit the GIF format itself stores. The awk step adds them up and divides by 100 to get seconds. A 12-frame GIF at 8 centiseconds per frame totals 96 centiseconds, so 0.96 seconds per loop. Note that very small delays are unreliable in practice: most browsers clamp anything below about 2 centiseconds up to a default, so a GIF's measured delay total can run faster than what you actually see on screen.
FAQ
See also
- The ffmpeg command cheat sheet: the hub reference for converting, trimming, and inspecting video and media from the CLI.
- Control an animated GIF's loop count: make a GIF play once, a fixed number of times, or forever with gifsicle.
- Get image dimensions from the command line: read width and height for any image format with ImageMagick, sips, or exiftool.
Sources
Authoritative references this article was fact-checked against.
- gifsicle manual (official)lcdf.org
- ImageMagick format and print escapes (official docs)imagemagick.org
- ImageMagick identify (official docs)imagemagick.org





