Skip to content
Image KB Reducer
Esc
↑↓navigate↵open⌘Jpreview
On this page

Output files

Where outputs go, how names are built, and why your original is never at risk.

The rule

The input is never overwritten unless you pass --in-place. Every write goes through an atomic guard designed around one scenario: someone runs this over the only copy of a photo they have.

Default naming

Without --out or --in-place, the output is written next to the input:

photo.jpg → photo-compressed.jpg

The pattern is <sanitized base><suffix>.<ext>:

  • The base comes from the input’s filename, run through the same filename sanitizer the website’s own download uses (one sanitiser, not two). It deletes invisible/bidi-override characters (the kind that can make photo‮gnp.exe render as photo exe.png while staying an executable) and a small set of Windows-reserved characters, but otherwise preserves your filename exactly — CJK, Devanagari, Arabic, Cyrillic, accented Latin, emoji, spaces and punctuation all survive. 休暇写真.jpg becomes 休暇写真-compressed.jpg.
  • The suffix is -compressed by default, or whatever you pass to --suffix <text>. It must be a plain filename fragment — no path separator — or the tool refuses at parse time, before it ever builds a path.
  • The extension always matches the format that was actually encoded — never echoed back from the input’s own extension, even when the output format happens to equal the input’s format. A .jpeg in becomes .jpg out (this tool’s own extension for the jpeg format), and a --format webp conversion of a .png gets a .webp extension, not a renamed .png.

--out <dir>

Writes to <dir> instead of next to the input, using the same naming otherwise:

imagekbreducer compress src/*.jpg --to 80kb --out dist/img

--in-place

The only flag that overwrites the input. Refused outright when the input is a symlink — replacing a symlink with rename/link (see below) would swap out the link, leaving the file it used to point to untouched and unreachable under that name, which is exactly the “destroyed the only copy” failure this tool exists to prevent, just reached through an alias instead of the same path twice. Pass --out <dir> instead for a symlinked input.

--suffix <text>

Overrides the default -compressed suffix. Combine with --out or use alone:

imagekbreducer compress photo.jpg --to 50kb --suffix -small
# photo-small.jpg

Refusing to clobber an existing output

If the computed output path already exists and isn’t the input itself, the tool refuses (exit 4) rather than silently replacing a file you didn’t ask it to touch:

imagekbreducer: refusing to overwrite existing file 'photo-compressed.jpg' -- pass --force to
replace it, or --out <dir> / --suffix <text> to write somewhere else instead

--force authorizes the overwrite. It only relaxes this check — it never bypasses the input-overwrite guard above; --in-place is the only way to do that. If the blocking path is a directory rather than a file, the message doesn’t offer --force (replacing a directory this way would just fail with a different, more confusing error) — only --out/--suffix.

Refusing to clobber another input in the same run

There is a second exit-4 refusal that --force does not relax: a destination that resolves to a different file this same command was asked to read.

imagekbreducer: refusing to overwrite 'photo-compressed.jpg' -- it is also an input to this run,
and --force replaces an existing output, not a file this same command was asked to read; pass
--out <dir> or --suffix <text> to write somewhere else instead

ikb compress photo.jpg photo-compressed.jpg --to 5kb --force is the shape: the first file’s output lands on the second, which processing would then read back from its own replacement and report as a second success over what was really one source. A glob across a directory that already holds a previous run’s outputs reaches it just as easily as typing both names. It is refused rather than warned about — a warning arrives on stderr after the bytes are gone, and --quiet silences it — and the message names the two flags that express the “write it somewhere else” intent instead. Paths are compared after resolving symlinks, not by the spelling you typed, and the input at risk counts whether it appears before or after on the command line.

--force keeps its actual job: it still replaces a stale existing output that isn’t an input. --in-place, which targets the input deliberately, is exempt.

Atomic writes

A write never appears under its final name until it’s complete. The tool writes to a temporary sibling file first, then makes that file visible under the destination name in one filesystem operation (link, which POSIX guarantees fails atomically if the destination already exists — no window for a second process to race into). If the process dies, the disk fills, or the encoder produces a truncated buffer partway through, the destination is left exactly as it was — never a half-written file sitting under a name that looks like a finished result.

Globs

Each input containing *, ?, [, or { is expanded with a real filesystem glob; anything else is used as a literal path. A literal filename that happens to contain a glob character (a real screenshot tool can produce Screenshot [1].png) is tried as a literal path first — only when no file exists under the exact string typed does the tool fall back to interpreting it as glob syntax. A glob that matches nothing exits 3 (not-found), the same as a literal path that doesn’t exist.

--dry-run

Does everything except write — no directory created, no temp file, no filesystem mutation at all — but the reported/JSON byte counts (resultBytes, the percentage saved) are the real numbers a real run would produce, computed the same way. The --dry-run overwrite prediction uses the same rules the real write does, including for a dangling symlink at the destination, so a dry run’s predicted exit code matches what the real run would do.

Was this page helpful?