rsync -avn, find … -print and git clean -nd print exactly what the destructive version would do, and each is one flag away from the real thing. It is the same habit every time: run the command in show-me mode, read the list, then remove the flag. rm is the exception — it has no such mode, and the workaround matters.
1. rsync: -n shows what would be copied
$ clai show what rsync would copy without copying it→ rsync -avn src/ backup/sending incremental file listapp.pyutil.pylogs/logs/app.logsent 159 bytes received 29 bytes 376.00 bytes/sectotal size is 8 speedup is 0.04 (DRY RUN)
The last two words, (DRY RUN), are the only difference in the output. The file list is identical to the real run — drop the -n and exactly these files get copied.
2. rsync --delete: the combination that bites
$ clai show what rsync would delete in the destination→ rsync -avn --delete src/ backup/sending incremental file listdeleting old-report.csvdeleting obsolete.py./notes.txtutil.pylogs/old.log
The deleting lines are the reason to bother with -n at all. Without it those two files vanish silently — verified: the same run without -n printed the same two lines and removed them.
3. find: -print first, -delete second
$ clai show logs older than 30 days without deleting them→ find src -name '*.log' -mtime +30 -printsrc/logs/old.log
find evaluates its tests left to right, so -print at the end shows precisely the list -delete would get. Never reorder them: -delete -name '*.log' deletes the whole tree first and filters afterwards.
4. git clean: -n prints what it would remove
$ clai show which untracked files git clean would remove→ git clean -ndWould remove backup/Would remove src/notes.txtWould remove tmpdir/
-n is the check, -d includes directories. Running git clean -fd without the -nd pass first is the quickest way to lose a file you forgot to stage.
5. sed: the preview is sed without -i
$ clai show how the replacement in conf.ini would look without changing the file→ sed 's/old/new/' conf.inihost=newport=80
Without -i, sed prints the result to the terminal and leaves the file alone — that is its built-in dry run. Verified: cat conf.ini afterwards still shows host=old.
6. When the edit does go in, keep a copy
$ clai replace in conf.ini but keep a backup→ sed -i.bak 's/old/new/' conf.ini
You get conf.ini.bak next to it with the original content. On macOS the suffix is mandatory even when empty (-i '') — the detail that breaks scripts moved over from Linux.
Gotchas
rmhas no dry run. The substitute is the same glob withls:ls -1 src/logs/*.logshows exactly whatrm src/logs/*.logwould take.echo rm …works too, printing the full command instead of running it.-nmeans different things to different commands. For rsync it's dry run, forgrep -nline numbers, forsort -nnumeric sort. Don't carry the habit across blindly: find calls it-print, git calls it--dry-run.- The check and the real run must differ by exactly one flag. If you also changed the path, the pattern or the argument order in between, you verified a different command. Delete the
-nfrom the line you already typed instead of typing it again.
Related questions
Do package managers have a dry run? Yes: apt-get -s install pkg shows the plan without installing, dnf has --assumeno, and docker has docker system prune --dry-run.
How do I check what a script will do? bash -n script.sh checks the syntax without running anything; bash -x script.sh prints each command as it runs — note that it does run them.
Can I undo rsync --delete? No. The protections are -n beforehand, or --backup --backup-dir=…, which moves the doomed files aside instead of deleting them.
See also
- Delete files older than N days
- Copy files preserving the directory structure
- Find and replace across files
CliAI labels a destructive command CAUTION or DANGER and offers the dry-run form first, before anything runs. Install it in one line.