Half of ad-hoc backups look like the same shape: take a directory, compress it, push it somewhere off this host. Nobody loves writing the pipe out by hand. Date-stamped names, gzip levels, the right S3 flags — there's enough surface to pause for.
The full pipe in one prompt
$ clai tar this folder, gzip it with a date suffix, and upload to s3://team-backups/db/→ tar -czf - . \ | aws s3 cp - "s3://team-backups/db/$(basename "$PWD")-$(date +%Y%m%d-%H%M).tar.gz"upload: <stdin> to s3://team-backups/db/data-20260427-1432.tar.gz
A few things worth noticing. The archive is streamed — tar -czf - writes to stdout, aws s3 cp - reads from stdin — so no local intermediate file. The S3 key includes both the directory name and a timestamp, which is what most teams actually want for a backup naming scheme.
Add a "delete on success" guard
The natural follow-up: clean up the source after the upload succeeded.
$ clai do the same upload, and remove the local folder if the upload returns 0→ tar -czf - . \ | aws s3 cp - "s3://team-backups/db/$(basename "$PWD")-$(date +%Y%m%d-%H%M).tar.gz" \ && rm -rf -- "$PWD"[Safety: DANGER] — recursive deletion of working directory.
CliAI flagged the second variant DANGER because of the trailing rm -rf -- "$PWD". The classifier looks at the whole pipeline, not just the first stage. The safety post walks through how that decision is made.
When the right answer isn't S3
Swap the destination — "to gs://my-bucket", "to a remote host via rsync", "to my Backblaze B2 bucket" — and the structure stays the same. The shell pipeline doesn't care about the URL; the model picks the right uploader.
See also
- Cleanup old logs and backups — the rotation half of the problem.
- SSH and rsync remote tasks — when the destination is another machine, not a bucket.