← All AI Scripts Restore · bash

extracttodisk

Unpacks backup archives back onto disk, and shows you exactly which files would be overwritten before it touches anything. The companion to backuptousb, but it accepts any tar archive.

Free to use Linux + bash .tar.zst / .tar.gz / .tar No installation

The problem it solves

Restoring is more dangerous than backing up. A backup writes to an empty disk. A restore writes on top of what you have, and done on autopilot it can replace a fresh folder with a three-week-old copy without you noticing until it is too late.

Plain tar -x says nothing. It just writes, and files that were there before disappear without a word. This script reads the archive index first, compares it against what is actually on disk, and puts the facts in front of you before anything happens.

Getting started

bash
$ chmod +x ~/scripts/extracttodisk
$ echo "alias extracttodisk='~/scripts/extracttodisk'" >> ~/.bashrc
$ source ~/.bashrc

The default restore folder is set at the top of the file:

extracttodisk, EDIT ME
DEFAULT_RESTORE="/home/youruser/backuprestore"   # where archives get unpacked

The default is deliberately not your home directory. Archives hold relative paths, so an archive called myproject.tar.zst creates backuprestore/myproject/. Your live ~/myproject/ is left untouched, and you can compare the two before moving anything back.

Usage

extracttodiskWith no arguments: takes the newest archive sitting next to the script itself. On a backup disk that is usually exactly what you want.
extracttodisk a.tar.zstOne named archive.
extracttodisk *.tar.zstEverything in the folder, one after another, into the same destination.
-lLists the contents of the archive and stops there. Extracts nothing.
-qSkips reading the archive index. Starts immediately, but with no collision list.
-yNo prompts. Always picks the safe option.
-o <path>Extract somewhere other than the default folder.
-hThe whole comment block from the top of the file.

See what is in the archive first

If you are not sure what you have, list it. Nothing is written to disk:

extracttodisk -l
$ extracttodisk notes.txt.tar.zst -l
notes.txt

A typical restore

If the destination folder does not exist, it offers to create it rather than aborting:

extracttodisk myproject.tar.zst
$ extracttodisk myproject.tar.zst

Restore into (Enter to accept): /home/youruser/backuprestore

Folder does not exist: /home/youruser/backuprestore
Create it? [Y/n] y
Created /home/youruser/backuprestore

Reading archive index (use -q to skip)... 55 files

Extracting  myproject.tar.zst
        ->  /home/youruser/backuprestore/myproject/

[#][#][#][#][#][#][#][#][#][#][#][#][#][#][#][#][#][#][#][#][#][#][#][#][#][#][#][#] 100%  00:00 taken

Flushing to disk... done

  Archive   689  (55 files)
  Restored  220K  ->  /home/youruser/backuprestore/myproject
  Time      0m 0s
  Speed     (too fast to measure)

Scripts in the restore folder:
  + /home/youruser/backuprestore/extracttodisk
  + /home/youruser/backuprestore/backuptousb

The collision warning, the important part

Run the same restore again, on top of a folder that is already there, and it stops. It does not guess whether the folder "is the same one", because that is not decidable. It shows you the numbers and lets you choose:

collision
$ extracttodisk myproject.tar.zst

Reading archive index (use -q to skip)... 55 files

/home/youruser/backuprestore/myproject already exists:
  on disk now          220K   (52 files would be overwritten)
  incoming archive      689   packed 2026-08-05 12:15

Would OVERWRITE these existing files. First few:
   myproject/src/file1.js
   myproject/src/file26.js
   myproject/src/file39.js
   myproject/src/file22.js
   myproject/src/file17.js
   myproject/src/file11.js
   ... and 46 more

 [o] overwrite in place
 [n] extract to a fresh  myproject-restored-N/
 [s] skip this archive
Choose [o/N/s]: _

Note that the capital letter in [o/N/s] is N. Press Enter and the data lands in a new folder with nothing overwritten. Run it with -y, or without a terminal, from cron or through a pipe, and it takes that same safe route entirely on its own, and says so:

extracttodisk -y
Non-interactive: extracting to a fresh folder to avoid overwrite.
Extracting to a fresh folder: myproject-restored-2/

  Archive   689  (55 files)
  Restored  220K  ->  /home/youruser/backuprestore/myproject-restored-2

Run it a third time and you get myproject-restored-3. It counts upward until it finds a free name, so an automated run can never eat its own previous restore.

Details worth knowing

Fresh folders go via a staging directory, not tar --transform

The obvious way to extract under a new name is tar --transform. It is also a trap: the expression is sed syntax, and folder names containing regex metacharacters, something as ordinary as my[test], make the rename fail silently and the contents land straight on top of the existing folder. Which is exactly the overwrite you were trying to avoid. The script extracts into a temporary directory instead and moves the result into place afterwards. No regex, no surprises.

The archive index is read exactly once

Both the file count for the progress bar and the collision check need the list of what is in the archive. It is read once and cached. On a large archive that takes a while: a 50 GB .zst can sit at "Reading archive index" for minutes. If you know the destination is empty, -q skips the whole pass and starts immediately, with a spinner instead of a bar.

Interruptions clean up after themselves

Ctrl-C mid-restore removes both the cache in /tmp and a half-finished staging directory under the destination. You are not left with a .extracttodisk-stage.XXXX full of half files.

Both scripts copy themselves. Every run drops both backuptousb and extracttodisk next to the data: on the backup disk when you pack, in the restore folder when you unpack. The copies are overwritten whenever they differ, so the tool on the disk is always the same version that made the archives. A backup disk you find again in two years carries what it takes to unpack itself.

The raw command behind it

tar
# unpack
$ tar -I zstd -xf myproject.tar.zst -C /home/youruser/backuprestore

# just list the contents
$ tar -I zstd -tf myproject.tar.zst | less

# pull out a single directory from the archive
$ tar -I zstd -xf myproject.tar.zst myproject/src/

# check the archive is not corrupt
$ zstd -t myproject.tar.zst

Not maintained, change it yourself

Written for my own machine, published as-is. No updates, no support.

If you want it to behave differently, download the file and hand it to an AI along with what you want: "extract straight into my home directory", "never ask, always overwrite", "verify checksums afterwards". The code is commented to be read, both by you and by whichever model you ask.