I wanted what every other file manager seems to ship with: select a few files, right-click, Send to .zip, done. Thunar — XFCE’s file manager — doesn’t have it out of the box, but it has something better: a clean, scriptable extension point called Custom Actions. Here’s how I wired it up on this machine, and how you can do the same by hand.
The idea
A Thunar Custom Action is just a menu entry that runs a shell command, with the current selection passed in as arguments. Thunar substitutes a few tokens for you:
| Token | Meaning |
|---|---|
%f |
first selected item, absolute path |
%F |
all selected items, absolute paths |
%n |
first selected item, basename only |
%N |
all selected items, basenames only |
%d |
directory containing the selection |
You can cram the whole zip command into that one field. I didn’t — inline shell quoting inside an XML attribute is a quoting nightmare and impossible to tweak later. Instead I put the logic in a tiny script and pointed the action at it. The action becomes a one-liner; all the real work lives somewhere editable.
What I actually did
This machine already had everything it needs:
thunar 4.20.7 (Xfce 4.20)
/usr/bin/zip
/usr/bin/7z
~/.local/bin (exists and is on $PATH)
1. The helper script
I dropped this at ~/.local/bin/thunar-zip and made it executable
(chmod +x):
#!/usr/bin/env bash
# thunar-zip — "Send to .zip" helper for Thunar custom actions.
# Called as: thunar-zip FILE [FILE...] (absolute paths, i.e. Thunar's %F)
# Creates an archive next to the selection. If one item is selected the
# archive is named after it; if several are selected it is named after the
# parent folder. Zips basenames only so the archive has no leading path junk.
set -euo pipefail
[ "$#" -ge 1 ] || { echo "thunar-zip: no files given" >&2; exit 1; }
first=$1
dir=$(dirname -- "$first")
cd -- "$dir"
if [ "$#" -eq 1 ]; then
base=$(basename -- "$first")
out="${base%.*}.zip"
[ "$out" != ".zip" ] || out="$base.zip" # dotfiles / no extension
else
out="$(basename -- "$dir").zip"
fi
# Don't clobber: append -1, -2, ... if the target already exists.
if [ -e "$out" ]; then
stem=${out%.zip}; n=1
while [ -e "${stem}-${n}.zip" ]; do n=$((n + 1)); done
out="${stem}-${n}.zip"
fi
# Feed basenames via stdin (-@) so names with spaces are handled safely.
for f in "$@"; do basename -- "$f"; done | zip -r "$out" -@
Three decisions worth calling out:
- Basenames, not full paths. If you just run
zip archive.zip %F, the archive ends up with the entire absolute path baked in (home/user/Documents/...). Feeding basenames viazip -@(read names from stdin) keeps the archive flat and clean, and stdin-with-one-name-per-line survives spaces in filenames where a plain argument list might not. - Smart naming. One file →
report.pdfbecomesreport.zip. Several files → archive named after the folder they live in. - No clobbering. Run it twice and you get
report.zip, thenreport-1.zip, never an overwrite.set -euo pipefailmeans it bails loudly instead of producing a half-broken archive.
2. The Thunar action
Custom actions live in ~/.config/Thunar/uca.xml. I appended one <action>
block (leaving the existing entries untouched), just before the closing
</actions>:
<action>
<icon>package-x-generic</icon>
<name>Send to .zip</name>
<submenu></submenu>
<unique-id>1782000000000000-1</unique-id>
<command>thunar-zip %F</command>
<description>Compress the selected file(s) into a .zip archive</description>
<range>*</range>
<patterns>*</patterns>
<directories/>
<audio-files/>
<image-files/>
<other-files/>
<text-files/>
<video-files/>
</action>
The <...-files/> and <directories/> tags are the Appearance
Conditions — they decide which kinds of selections show the menu entry.
All of them listed = it shows for everything, including folders. %F passes
the whole selection to the script.
3. Verify
I smoke-tested the script directly before trusting it:
$ thunar-zip /tmp/a.txt "/tmp/b file.txt"
adding: a.txt (stored 0%)
adding: b file.txt (stored 0%)
The archive contained a.txt and b file.txt — flat, spaces intact, no
path junk. A second run on the same target correctly produced ...-1.zip.
How to do this yourself (the GUI way)
If you’d rather not touch XML, Thunar has a full editor for this:
- Open Thunar → Edit → Configure custom actions…
- Click the + (Add) button.
- Basic tab:
- Name:
Send to .zip - Description:
Compress the selected file(s) into a .zip archive - Command:
thunar-zip %F(or, if you skip the helper script, an inline version — see below) - Icon: pick something like package-x-generic.
- Name:
- Appearance Conditions tab:
- File Pattern:
* - Tick every “Appears if selection contains” box (directories, audio, image, text, video, other files) so it shows for any selection.
- File Pattern:
- OK. The entry appears immediately — no restart, no logout.
If you don’t want a separate script, you can put the logic straight in the Command field. A serviceable inline version:
sh -c 'cd "$(dirname "$1")"; for f in "$@"; do basename -- "$f"; done | zip -r "archive.zip" -@' _ %F
…but that always names the archive archive.zip and the quoting is fiddly,
which is exactly why I preferred the script.
How to do this yourself (the file-editing way)
- Save the script to
~/.local/bin/thunar-zipand runchmod +x ~/.local/bin/thunar-zip. (Make sure~/.local/binis on your$PATH—echo $PATHto check.) - Open
~/.config/Thunar/uca.xmlin a text editor. - Paste the
<action>…</action>block from above just before the final</actions>line. Give it a<unique-id>that isn’t already used. - Save. If Thunar is already running, the menu picks it up the next time you
right-click; if not,
thunar -qthen reopen forces a reload.
The full menu
One action is lonely, so I built out a small toolbox. The archive actions
group under a Compress submenu and the image ones under Images (set
the <submenu> field — same value on several actions nests them together);
the rest sit at the top level. Every one follows the same pattern: a tiny
script in ~/.local/bin, an <action> in uca.xml pointing at it.
| Menu entry | Script | What it does |
|---|---|---|
| Compress → Send to .zip | thunar-zip |
zip archive, basenames, smart-named, no clobber |
| Compress → Send to .7z | thunar-7z |
same, but .7z (better compression) |
| Compress → Extract here | thunar-extract |
extracts each archive into its own subfolder (tarbomb-safe) |
| Images → Resize to 1080p (copy) | thunar-img-resize |
non-destructive copy shrunk to fit 1920×1080 |
| Images → Strip metadata (copy) | thunar-img-strip |
non-destructive copy with EXIF/GPS removed |
| Images → Set as wallpaper | thunar-setwallpaper |
sets the XFCE desktop backdrop |
| Copy path to clipboard | thunar-copypath |
absolute path(s) → clipboard (needs xclip) |
| SHA-256 checksum | thunar-sha256 |
writes CHECKSUMS.sha256 beside the selection |
| Open as root | pkexec thunar %f |
root file-manager window (inline, no script) |
A few design choices that recur across these:
- Non-destructive by default. The image actions never edit your
originals — they write
name-1080p.png/name-stripped.pngcopies. A right-click that silently mangles your only copy of a photo is a bad right-click. - Tarbomb-safe extraction.
thunar-extractmakes a folder per archive and unpacks into it, so a badly-packed archive can’t litter your Downloads folder with 400 loose files. %fvs%F. Set as wallpaper uses%f(single file) and<range>1</range>so it only appears for one selected image. Everything else uses%Fto act on the whole selection.- Graceful dependencies. Copy path needs
xclip(orxsel), which isn’t installed here yet — the script printssudo apt install xclipinstead of failing silently.
The new helper scripts
~/.local/bin/thunar-7z — identical in spirit to thunar-zip; it
passes basenames as a shell array to 7z a so spaces survive and no path
junk leaks in.
~/.local/bin/thunar-extract
#!/usr/bin/env bash
set -euo pipefail
[ "$#" -ge 1 ] || { echo "thunar-extract: no files given" >&2; exit 1; }
for arc in "$@"; do
[ -f "$arc" ] || { echo "skip (not a file): $arc" >&2; continue; }
dir=$(dirname -- "$arc"); name=$(basename -- "$arc")
case "$name" in
*.tar.gz|*.tgz) stem=${name%.tar.gz}; stem=${stem%.tgz} ;;
*.tar.bz2|*.tbz2|*.tbz) stem=${name%.tar.bz2}; stem=${stem%.tbz2}; stem=${stem%.tbz} ;;
*.tar.xz|*.txz) stem=${name%.tar.xz}; stem=${stem%.txz} ;;
*.tar.zst|*.tzst) stem=${name%.tar.zst}; stem=${stem%.tzst} ;;
*.tar) stem=${name%.tar} ;;
*) stem=${name%.*} ;;
esac
dest="$dir/$stem"
if [ -e "$dest" ]; then n=1; while [ -e "${dest}-${n}" ]; do n=$((n+1)); done; dest="${dest}-${n}"; fi
mkdir -p -- "$dest"
case "$name" in
*.tar|*.tar.*|*.tgz|*.tbz2|*.tbz|*.txz|*.tzst) tar -xf "$arc" -C "$dest" ;;
*) 7z x -y -o"$dest" -- "$arc" >/dev/null ;;
esac
echo "Extracted -> $dest"
done
~/.local/bin/thunar-img-resize — convert "$f" -resize '1920x1080>'
"<name>-1080p.<ext>". The trailing > is the magic bit: ImageMagick only
shrinks, so a small image is left alone instead of being blown up and
blurred.
~/.local/bin/thunar-img-strip — convert "$f" -strip
"<name>-stripped.<ext>". Handy before posting a photo publicly, since phone
JPEGs carry GPS coordinates.
~/.local/bin/thunar-setwallpaper — this version of xfdesktop has no
--set-wallpaper flag, so the script walks every backdrop property with
xfconf-query and reloads:
xfconf-query -c xfce4-desktop -l | grep '/last-image$' | while read -r prop; do
xfconf-query -c xfce4-desktop -p "$prop" -s "$img"
done
xfdesktop --reload
~/.local/bin/thunar-sha256 — writes CHECKSUMS.sha256 (basenames, so
the file verifies with a plain sha256sum -c CHECKSUMS.sha256).
~/.local/bin/thunar-copypath — tries xclip, then xsel, then tells
you what to install.
All of them start with set -euo pipefail (fail loud, never produce a
half-finished result) and were smoke-tested from the command line before
being trusted in the menu.
Tools you’ll need
On this machine these were already present: zip, 7z, tar, sha256sum,
ImageMagick (convert/mogrify), xfconf-query, xfdesktop, pkexec.
The only gap was the clipboard helper:
sudo apt install xclip
Going further
The whole pattern — menu entry → small script on your PATH — generalizes to anything: upload to a server, open in a specific editor, run a linter, convert audio. Thunar just hands your script the selection and gets out of the way. Some natural next additions:
- More archive formats — clone
thunar-zipfor.tar.gzor.tar.zst. - Checksum verify — an action on
*.sha256runningsha256sum -c %f. - Run in a terminal — for long or interactive jobs, launch via
xfce4-terminal --hold -x your-command %Fso the output window stays open.
To edit any of this later: the scripts live in ~/.local/bin/thunar-* and
the menu definitions in ~/.config/Thunar/uca.xml (or use Edit →
Configure custom actions… in Thunar). Changes take effect on the next
right-click — no restart required.