tar.md (33351B)
1 --- 2 title: "TAR" 3 description: "tar [OPERATION] [OPTIONS] -f ARCHIVE [FILES...]" 4 category: tools 5 tags: ["tools"] 6 tools: ["GPG", "OpenSSL"] 7 difficulty: intermediate 8 updated: "2026-08-10" 9 source: "vault:Tools/TAR.md" 10 --- 11 # The Ultimate `tar` Cheat Sheet 12 13 > GNU tar 1.35 | Last updated: April 2026 14 15 --- 16 17 ## Core Syntax 18 19 ``` 20 tar [OPERATION] [OPTIONS] -f ARCHIVE [FILES...] 21 ``` 22 23 The `-f` flag tells tar you're working with files, not a tape device (the `f` is always required). 24 25 --- 26 27 ## Operations (pick one) 28 29 |Flag|Long Form|Purpose| 30 |---|---|---| 31 |`c`|`--create`|Create a new archive| 32 |`x`|`--extract`|Extract files from an archive| 33 |`t`|`--list`|List contents of an archive| 34 |`r`|`--append`|Append files to the end of an archive (uncompressed only)| 35 |`u`|`--update`|Append files newer than the copy in the archive (uncompressed only)| 36 |`d`|`--diff` / `--compare`|Compare archive members against the filesystem| 37 |`A`|`--concatenate`|Append one tar archive to another| 38 |`--delete`||Delete members from the archive (uncompressed only)| 39 40 --- 41 42 ## Common Modifier Flags 43 44 |Flag|Long Form|Purpose| 45 |---|---|---| 46 |`v`|`--verbose`|Verbose output (list files processed)| 47 |`vv`||Extra verbose (show permissions, ownership, size)| 48 |`f`|`--file`|Specify archive filename| 49 |`C`|`--directory`|Change to directory before performing operation| 50 |`p`|`--preserve-permissions`|Preserve file permissions on extraction| 51 |`P`|`--absolute-names`|Don't strip leading `/` from paths| 52 |`k`|`--keep-old-files`|Don't overwrite existing files on extraction| 53 |`--overwrite`||Overwrite existing files on extraction| 54 |`w`|`--interactive`|Ask for confirmation for every action| 55 |`--same-owner`||Try to extract with the same ownership| 56 |`--no-same-owner`||Extract as current user| 57 |`--numeric-owner`||Use numeric UID/GID (useful for cross-system restores)| 58 |`--acls`||Preserve POSIX ACLs| 59 |`--selinux`||Preserve SELinux contexts| 60 |`--xattrs`||Preserve extended attributes| 61 |`-h`|`--dereference`|Follow symlinks (archive the target file, not the link)| 62 |`--hard-dereference`||Follow hard links| 63 |`--one-file-system`||Stay on one filesystem (don't cross mount points)| 64 |`-S`|`--sparse`|Handle sparse files efficiently| 65 |`--totals`||Print total bytes after processing| 66 |`--checkpoint=N`||Print a progress message every N records| 67 68 --- 69 70 ## All Compression Types 71 72 GNU tar supports 8 compression filters. Each can be used with `-c` (create) or `-x` (extract). 73 74 ### Quick Reference 75 76 |Algorithm|Short Flag|Long Flag|Extension|Compression Ratio|Speed|Levels| 77 |---|---|---|---|---|---|---| 78 |gzip|`-z`|`--gzip`|`.tar.gz` / `.tgz`|Good|Fast|1-9| 79 |bzip2|`-j`|`--bzip2`|`.tar.bz2` / `.tbz2`|Better|Slow|1-9| 80 |xz (LZMA2)|`-J`|`--xz`|`.tar.xz` / `.txz`|Best|Slowest|0-9 (+`-e` extreme)| 81 |zstd|`--zstd`|`--zstd`|`.tar.zst`|Good-Great|Very Fast|1-19 (+`--ultra` to 22)| 82 |lzip|`--lzip`|`--lzip`|`.tar.lz`|Best|Slow|0-9| 83 |lzma|`--lzma`|`--lzma`|`.tar.lzma`|Great|Slow|0-9| 84 |lzop|`--lzop`|`--lzop`|`.tar.lzo`|Lower|Very Fast|1-9| 85 |compress|`-Z`|`--compress`|`.tar.Z`|Poor|Fast|N/A (legacy)| 86 87 ### Create with Each Compression Type 88 89 ```bash 90 # gzip (most universal) 91 tar czf archive.tar.gz /path/to/dir 92 93 # bzip2 (legacy, still common in older tarballs) 94 tar cjf archive.tar.bz2 /path/to/dir 95 96 # xz (best compression, used by kernel tarballs and distro packages) 97 tar cJf archive.tar.xz /path/to/dir 98 99 # zstd (best speed-to-ratio balance, modern default) 100 tar --zstd -cf archive.tar.zst /path/to/dir 101 102 # lzip 103 tar --lzip -cf archive.tar.lz /path/to/dir 104 105 # lzma (predecessor to xz) 106 tar --lzma -cf archive.tar.lzma /path/to/dir 107 108 # lzop (ultrafast, low ratio) 109 tar --lzop -cf archive.tar.lzo /path/to/dir 110 111 # compress (legacy, avoid) 112 tar -Zcf archive.tar.Z /path/to/dir 113 114 # no compression (plain tarball) 115 tar cf archive.tar /path/to/dir 116 ``` 117 118 ### Extract with Each Type 119 120 ```bash 121 # gzip 122 tar xzf archive.tar.gz 123 124 # bzip2 125 tar xjf archive.tar.bz2 126 127 # xz 128 tar xJf archive.tar.xz 129 130 # zstd 131 tar --zstd -xf archive.tar.zst 132 133 # lzip 134 tar --lzip -xf archive.tar.lz 135 136 # auto-detect compression (GNU tar) 137 tar xaf archive.tar.gz # 'a' auto-detects the compressor 138 tar xf archive.tar.xz # GNU tar also auto-detects without 'a' in most cases 139 ``` 140 141 ### List Contents Without Extracting 142 143 ```bash 144 tar tzf archive.tar.gz # gzip 145 tar tjf archive.tar.bz2 # bzip2 146 tar tJf archive.tar.xz # xz 147 tar --zstd -tf archive.tar.zst # zstd 148 tar tf archive.tar # uncompressed 149 tar tf archive.tar.gz | head -20 # preview first 20 entries 150 tar tf archive.tar.gz | grep '\\.conf$' # search for .conf files 151 ``` 152 153 --- 154 155 ## Setting Compression Levels 156 157 The `-I` flag (or `--use-compress-program`) lets you pass custom arguments to the compressor, including compression levels. This is how you control the speed/size tradeoff. 158 159 ### Method 1: The `-I` Flag (Recommended) 160 161 ```bash 162 # gzip: levels 1 (fastest) to 9 (smallest), default 6 163 tar -I 'gzip -1' -cf archive.tar.gz /path/to/dir # fastest 164 tar -I 'gzip -6' -cf archive.tar.gz /path/to/dir # default 165 tar -I 'gzip -9' -cf archive.tar.gz /path/to/dir # smallest 166 167 # bzip2: levels 1-9, default 9 168 tar -I 'bzip2 -1' -cf archive.tar.bz2 /path/to/dir # fastest 169 tar -I 'bzip2 -9' -cf archive.tar.bz2 /path/to/dir # smallest (default) 170 171 # xz: levels 0-9, default 6. Add -e for extreme mode 172 tar -I 'xz -0' -cf archive.tar.xz /path/to/dir # fastest 173 tar -I 'xz -6' -cf archive.tar.xz /path/to/dir # default 174 tar -I 'xz -9' -cf archive.tar.xz /path/to/dir # smallest 175 tar -I 'xz -9e' -cf archive.tar.xz /path/to/dir # extreme (even smaller, much slower) 176 177 # zstd: levels 1-19, default 3. --ultra unlocks 20-22 178 tar -I 'zstd -1' -cf archive.tar.zst /path/to/dir # fastest 179 tar -I 'zstd -3' -cf archive.tar.zst /path/to/dir # default 180 tar -I 'zstd -19' -cf archive.tar.zst /path/to/dir # high compression 181 tar -I 'zstd --ultra -22' -cf archive.tar.zst /path/to/dir # maximum (memory heavy) 182 183 # lzip: levels 0-9, default 6 184 tar -I 'lzip -9' -cf archive.tar.lz /path/to/dir 185 186 # lzop: levels 1-9, default 3 187 tar -I 'lzop -9' -cf archive.tar.lzo /path/to/dir 188 ``` 189 190 ### Method 2: Environment Variables (gzip/bzip2/xz only) 191 192 ```bash 193 # gzip via GZIP env var (deprecated in newer gzip, but still works in most distros) 194 GZIP=-9 tar czf archive.tar.gz /path/to/dir 195 196 # xz via XZ_OPT 197 XZ_OPT='-9e' tar cJf archive.tar.xz /path/to/dir 198 199 # zstd via ZSTD_CLEVEL 200 ZSTD_CLEVEL=19 tar --zstd -cf archive.tar.zst /path/to/dir 201 ``` 202 203 --- 204 205 ## Multi-threaded / Parallel Compression 206 207 Single-threaded compression is painfully slow on large datasets. Use parallel implementations to utilise all your cores. 208 209 ### Native Multi-threading 210 211 ```bash 212 # xz with -T0 (use all available cores, supported since xz 5.2+) 213 tar -I 'xz -9e -T0' -cf archive.tar.xz /path/to/dir 214 215 # zstd with -T0 (native multi-threading, default since zstd 1.5.7) 216 tar -I 'zstd -19 -T0' -cf archive.tar.zst /path/to/dir 217 218 # zstd with explicit thread count 219 tar -I 'zstd -19 -T4' -cf archive.tar.zst /path/to/dir 220 ``` 221 222 ### Drop-in Parallel Replacements 223 224 ```bash 225 # pigz (parallel gzip, fully compatible output) 226 tar -I 'pigz -9' -cf archive.tar.gz /path/to/dir 227 tar -I 'pigz -9 -p 4' -cf archive.tar.gz /path/to/dir # limit to 4 cores 228 229 # pbzip2 (parallel bzip2) 230 tar -I 'pbzip2 -9' -cf archive.tar.bz2 /path/to/dir 231 tar -I 'pbzip2 -9 -p4' -cf archive.tar.bz2 /path/to/dir 232 233 # lbzip2 (alternative parallel bzip2, often faster decompression) 234 tar -I lbzip2 -cf archive.tar.bz2 /path/to/dir 235 236 # plzip (parallel lzip) 237 tar -I 'plzip -9' -cf archive.tar.lz /path/to/dir 238 ``` 239 240 ### Install Parallel Tools 241 242 ```bash 243 # Debian/Ubuntu 244 sudo apt install pigz pbzip2 lbzip2 zstd 245 246 # RHEL/Fedora 247 sudo dnf install pigz pbzip2 lbzip2 zstd 248 249 # Arch 250 sudo pacman -S pigz pbzip2 lbzip2 zstd 251 252 # macOS 253 brew install pigz pbzip2 lbzip2 zstd 254 ``` 255 256 --- 257 258 ## Splitting Archives into Parts 259 260 For transferring over networks, fitting onto FAT32 drives (4GB limit), or uploading in chunks. 261 262 ### Create and Split in One Pipeline 263 264 ```bash 265 # Split a gzip archive into 100MB chunks 266 tar czf - /path/to/dir | split -b 100M - archive.tar.gz.part- 267 268 # Split an xz archive into 500MB chunks with numeric suffixes 269 tar cJf - /path/to/dir | split -b 500M -d - archive.tar.xz.part- 270 271 # Split a zstd archive into 1GB chunks 272 tar --zstd -cf - /path/to/dir | split -b 1G -d - archive.tar.zst.part- 273 274 # Split with a custom number of digits in suffix 275 tar czf - /path/to/dir | split -b 100M -d -a 3 - archive.tar.gz.part- 276 # produces: archive.tar.gz.part-000, archive.tar.gz.part-001, ... 277 ``` 278 279 ### Split an Existing Archive 280 281 ```bash 282 split -b 100M archive.tar.gz archive.tar.gz.part- 283 ``` 284 285 ### Reassemble and Extract 286 287 ```bash 288 # Reassemble into a single file, then extract 289 cat archive.tar.gz.part-* > archive.tar.gz 290 tar xzf archive.tar.gz 291 292 # Or pipe directly without creating the intermediate file 293 cat archive.tar.gz.part-* | tar xzf - 294 295 # For xz 296 cat archive.tar.xz.part-* | tar xJf - 297 298 # For zstd 299 cat archive.tar.zst.part-* | tar --zstd -xf - 300 ``` 301 302 ### Verify Split Archive Integrity 303 304 ```bash 305 # Check the reassembled archive is valid 306 cat archive.tar.gz.part-* | tar tzf - > /dev/null && echo "OK" || echo "CORRUPT" 307 308 # Generate checksums before transfer 309 sha256sum archive.tar.gz.part-* > checksums.sha256 310 311 # Verify after transfer 312 sha256sum -c checksums.sha256 313 ``` 314 315 ### GNU tar Native Multi-Volume (`-M`) 316 317 ```bash 318 # Create multi-volume archive (each volume max 100MB) 319 tar -cML 100M -f vol1.tar /path/to/dir 320 # tar will prompt for the next volume name when vol1 fills up 321 322 # Extract multi-volume 323 tar -xMf vol1.tar 324 # tar prompts for subsequent volumes 325 326 # Note: multi-volume archives CANNOT be compressed 327 # For compressed split archives, use the pipe method above 328 ``` 329 330 --- 331 332 ## Excluding Files and Directories 333 334 ```bash 335 # Exclude a single file or directory 336 tar czf archive.tar.gz --exclude='*.log' /path/to/dir 337 338 # Exclude multiple patterns 339 tar czf archive.tar.gz \\ 340 --exclude='*.log' \\ 341 --exclude='*.tmp' \\ 342 --exclude='.git' \\ 343 --exclude='node_modules' \\ 344 --exclude='__pycache__' \\ 345 /path/to/dir 346 347 # Exclude from a file (one pattern per line) 348 tar czf archive.tar.gz --exclude-from=exclude.txt /path/to/dir 349 350 # Exclude files matching a regex (GNU tar) 351 tar czf archive.tar.gz --exclude='./src/*.test.js' /path/to/dir 352 353 # Exclude version control directories 354 tar czf archive.tar.gz --exclude-vcs /path/to/dir 355 # Excludes: .git, .svn, .hg, .bzr, CVS, etc. 356 357 # Exclude version control ignores too (.gitignore, .hgignore, etc.) 358 tar czf archive.tar.gz --exclude-vcs-ignores /path/to/dir 359 360 # Exclude backup files (*~, #*#) 361 tar czf archive.tar.gz --exclude-backups /path/to/dir 362 363 # Exclude files if a certain file exists in the directory 364 tar czf archive.tar.gz --exclude-tag='.nobackup' /path/to/dir 365 366 # Exclude caches (directories containing CACHEDIR.TAG) 367 tar czf archive.tar.gz --exclude-caches /path/to/dir 368 ``` 369 370 --- 371 372 ## Extracting Specific Files 373 374 ```bash 375 # Extract a single file 376 tar xzf archive.tar.gz path/to/specific/file.txt 377 378 # Extract files matching a wildcard 379 tar xzf archive.tar.gz --wildcards '*.conf' 380 tar xzf archive.tar.gz --wildcards '*/nginx/*' 381 382 # Extract to a specific directory 383 tar xzf archive.tar.gz -C /opt/restore/ 384 385 # Extract only newer files (don't overwrite newer existing files) 386 tar xzf archive.tar.gz --keep-newer-files 387 388 # Extract and strip leading path components 389 tar xzf archive.tar.gz --strip-components=1 390 # e.g. project-v1.0/src/main.c extracts as src/main.c 391 392 tar xzf archive.tar.gz --strip-components=2 393 # e.g. project-v1.0/src/main.c extracts as main.c 394 ``` 395 396 --- 397 398 ## Incremental / Differential Backups 399 400 GNU tar supports incremental backups using a snapshot file that tracks filesystem state between runs. 401 402 ```bash 403 # Level 0: full backup (creates the snapshot file) 404 tar -g /backup/snapshot.snar -czf /backup/full-$(date +%F).tar.gz /home/user/ 405 406 # Level 1: incremental (only files changed since the last backup) 407 tar -g /backup/snapshot.snar -czf /backup/inc-$(date +%F).tar.gz /home/user/ 408 409 # To force a new full backup, delete or move the snapshot file 410 rm /backup/snapshot.snar 411 412 # Restore: apply full, then each incremental IN ORDER 413 tar -xzf /backup/full-2026-03-01.tar.gz -g /dev/null -C /restore/ 414 tar -xzf /backup/inc-2026-03-02.tar.gz -g /dev/null -C /restore/ 415 tar -xzf /backup/inc-2026-03-03.tar.gz -g /dev/null -C /restore/ 416 # Note: -g /dev/null on extract tells tar this is an incremental restore 417 # and it should handle file deletions properly 418 ``` 419 420 --- 421 422 ## Encryption 423 424 tar has no native encryption. Pipe through `gpg` or `openssl` to encrypt. 425 426 ### With GPG (Symmetric / Passphrase) 427 428 ```bash 429 # Create encrypted archive (prompts for passphrase) 430 tar czf - /path/to/dir | gpg -c --cipher-algo AES256 -o archive.tar.gz.gpg 431 432 # Decrypt and extract 433 gpg -d archive.tar.gz.gpg | tar xzf - 434 435 # With a specific recipient's public key (asymmetric) 436 tar czf - /path/to/dir | gpg -e -r recipient@example.com -o archive.tar.gz.gpg 437 438 # Decrypt (requires matching private key) 439 gpg -d archive.tar.gz.gpg | tar xzf - 440 ``` 441 442 ### With OpenSSL 443 444 ```bash 445 # Encrypt with AES-256-CBC (prompts for password) 446 tar czf - /path/to/dir | openssl enc -aes-256-cbc -salt -pbkdf2 -out archive.tar.gz.enc 447 448 # Decrypt and extract 449 openssl enc -d -aes-256-cbc -pbkdf2 -in archive.tar.gz.enc | tar xzf - 450 ``` 451 452 ### Encrypted Incremental Backup (GPG + tar) 453 454 ```bash 455 # Full backup, encrypted 456 tar -g snapshot.snar -czf - /home/user/ | gpg -c --cipher-algo AES256 -o backup-full.tar.gz.gpg 457 458 # Incremental, encrypted 459 tar -g snapshot.snar -czf - /home/user/ | gpg -c --cipher-algo AES256 -o backup-inc.tar.gz.gpg 460 461 # Restore 462 gpg -d backup-full.tar.gz.gpg | tar -xzf - -g /dev/null -C /restore/ 463 gpg -d backup-inc.tar.gz.gpg | tar -xzf - -g /dev/null -C /restore/ 464 ``` 465 466 ### Encrypted + Split 467 468 ```bash 469 # Create, compress, encrypt, and split into 100MB chunks 470 tar czf - /path/to/dir \\ 471 | gpg -c --cipher-algo AES256 \\ 472 | split -b 100M -d - archive.tar.gz.gpg.part- 473 474 # Reassemble, decrypt, extract 475 cat archive.tar.gz.gpg.part-* | gpg -d | tar xzf - 476 ``` 477 478 --- 479 480 ## Sending Archives Over the Network 481 482 ```bash 483 # Archive and transfer via SSH in one step 484 tar czf - /path/to/dir | ssh user@remote 'cat > /backup/archive.tar.gz' 485 486 # Extract remotely 487 tar czf - /path/to/dir | ssh user@remote 'tar xzf - -C /opt/deploy/' 488 489 # Pull from remote 490 ssh user@remote 'tar czf - /remote/dir' | tar xzf - -C /local/restore/ 491 492 # With zstd for speed 493 tar --zstd -cf - /path/to/dir | ssh user@remote 'tar --zstd -xf - -C /opt/deploy/' 494 495 # With progress bar (requires pv) 496 tar cf - /path/to/dir | pv | gzip | ssh user@remote 'cat > /backup/archive.tar.gz' 497 498 # Encrypted transfer (belt and braces with SSH) 499 tar czf - /path/to/dir | gpg -c --cipher-algo AES256 | ssh user@remote 'cat > /backup/archive.tar.gz.gpg' 500 ``` 501 502 --- 503 504 ## Comparing and Verifying Archives 505 506 ```bash 507 # Diff: compare archive members against the filesystem 508 tar dzf archive.tar.gz 509 # Shows files that differ between the archive and disk 510 511 # Verify archive integrity without extracting 512 tar tzf archive.tar.gz > /dev/null 513 echo $? # 0 = OK, non-zero = corrupted 514 515 # Test a zstd archive 516 tar --zstd -tf archive.tar.zst > /dev/null && echo "OK" || echo "CORRUPT" 517 518 # Generate a checksum of the archive 519 sha256sum archive.tar.gz > archive.tar.gz.sha256 520 521 # Verify 522 sha256sum -c archive.tar.gz.sha256 523 ``` 524 525 --- 526 527 ## Archive Formats 528 529 GNU tar can produce several archive formats. Usually you don't need to worry about this, but it matters for edge cases. 530 531 ```bash 532 # Specify format explicitly 533 tar --format=gnu -cf archive.tar /path/to/dir # GNU format (default) 534 tar --format=posix -cf archive.tar /path/to/dir # POSIX.1-2001 (pax) format 535 tar --format=ustar -cf archive.tar /path/to/dir # POSIX.1-1988 536 tar --format=v7 -cf archive.tar /path/to/dir # Old Unix V7 format 537 ``` 538 539 |Format|Long Filenames|Large Files (>8GB)|Extended Attributes|Notes| 540 |---|---|---|---|---| 541 |gnu|Yes|Yes|No|Default on Linux| 542 |posix (pax)|Yes|Yes|Yes|Most portable, recommended for cross-platform| 543 |ustar|255 chars max|No (8GB limit)|No|Older POSIX standard| 544 |v7|100 chars max|No|No|Legacy, avoid| 545 546 --- 547 548 ## Practical Combos and Recipes 549 550 ### Full System Backup 551 552 ```bash 553 tar -I 'zstd -9 -T0' -cpf /backup/system-$(date +%F).tar.zst \\ 554 --acls --selinux --xattrs \\ 555 --one-file-system \\ 556 --exclude='/proc/*' \\ 557 --exclude='/sys/*' \\ 558 --exclude='/dev/*' \\ 559 --exclude='/run/*' \\ 560 --exclude='/tmp/*' \\ 561 --exclude='/mnt/*' \\ 562 --exclude='/media/*' \\ 563 --exclude='/lost+found' \\ 564 --exclude='/backup/*' \\ 565 / 566 ``` 567 568 ### Web Server Backup 569 570 ```bash 571 tar -I 'zstd -12 -T0' -cf /backup/webserver-$(date +%F).tar.zst \\ 572 --exclude='*.log' \\ 573 --exclude='cache/*' \\ 574 --exclude='node_modules' \\ 575 /etc/nginx /etc/letsencrypt /var/www 576 ``` 577 578 ### Quick Grab of Specific File Types 579 580 ```bash 581 # Archive only .py files from a project 582 find /project -name '*.py' -print0 | tar czf python-files.tar.gz --null -T - 583 584 # Archive files modified in the last 24 hours 585 find /path -mtime -1 -print0 | tar czf recent-changes.tar.gz --null -T - 586 587 # Archive from a file list 588 tar czf archive.tar.gz -T filelist.txt 589 ``` 590 591 ### Benchmark Compression Algorithms 592 593 ```bash 594 for alg in 'gzip' 'bzip2' 'xz' 'zstd' 'zstd -19' 'xz -9e'; do 595 echo "--- $alg ---" 596 time tar -I "$alg" -cf /dev/null /path/to/test/dir 2>&1 597 echo 598 done 599 ``` 600 601 ### Disk Image Compression 602 603 ```bash 604 # Compress a raw disk image with zstd 605 dd if=/dev/sda bs=4M status=progress | zstd -T0 > disk-image.zst 606 607 # Restore 608 zstd -d disk-image.zst | dd of=/dev/sda bs=4M status=progress 609 ``` 610 611 ### Progress Bar with `pv` 612 613 ```bash 614 # Show progress while creating 615 tar cf - /large/directory | pv -s $(du -sb /large/directory | cut -f1) | gzip > archive.tar.gz 616 617 # Show progress while extracting 618 pv archive.tar.gz | tar xzf - 619 ``` 620 621 --- 622 623 ## Gotchas and Limitations 624 625 - **Compressed archives cannot be modified.** You cannot use `--update`, `--append`, or `--delete` on `.tar.gz`, `.tar.xz`, etc. Only uncompressed `.tar` files support these operations. 626 - **Multi-volume archives cannot be compressed.** Use the `split` pipe method instead. 627 - **Leading `/` is stripped by default.** This is a safety feature. Use `-P` to preserve absolute paths, but be careful on extraction. 628 - **Sparse file handling** requires `-S` to be passed explicitly. 629 - **Cross-platform gotchas:** GNU tar extensions (long filenames, ACLs, xattrs) may not be understood by BSD tar or busybox tar. Use `--format=posix` for maximum portability. 630 - **File ordering is not guaranteed** unless you sort your input file list. 631 - **xz multi-threaded compression uses a lot of RAM.** Roughly single-thread memory x thread count. Watch out on memory-constrained systems. 632 633 --- 634 635 ## When to Use What 636 637 | Scenario | Algorithm | Reasoning | 638 | ------------------------------------- | --------------- | ------------------------------------------------ | 639 | Daily backups | zstd (`-3 -T0`) | Fast, good ratio, multi-threaded by default | 640 | Long-term archival | xz (`-9e -T0`) | Best compression ratio, saves storage | 641 | Quick one-off / maximum compatibility | gzip | Available everywhere, fast enough | 642 | Software distribution | xz | Standard for kernel tarballs, distro packages | 643 | Real-time / filesystem compression | zstd | Used by btrfs, Fedora, Ubuntu, Arch for packages | 644 | Bandwidth-limited transfer | xz or zstd -19 | Minimise bytes on the wire | 645 | Speed-critical / huge datasets | lzop or zstd -1 | Minimal CPU overhead | 646 | Legacy systems / old tarballs | bzip2 | Superseded but still encountered | 647 648 --- 649 650 ## Portability: `-I` vs `--use-compress-program` vs Explicit Pipes 651 652 The `-I` flag behaves **differently** between GNU tar and BSD tar (macOS default). This is one of the most common causes of confusing errors. 653 654 ### The Problem 655 656 |Platform|`-I` Means| 657 |---|---| 658 |GNU tar (Linux)|`--use-compress-program` — run this external compressor| 659 |BSD tar / bsdtar (macOS)|`--include` — include files matching a pattern (same as `-T`)| 660 661 So on macOS: `tar -I 'xz -9e' -cf ...` will fail with `Couldn't open xz -9e: No such file or directory` because BSD tar is trying to read a **file list** called `xz -9e`. 662 663 ### Three Ways to Handle It 664 665 ```bash 666 # Method 1: --use-compress-program (works on BOTH GNU and BSD tar) 667 tar -c --use-compress-program='xz -9e' -f - /path/to/dir > archive.tar.xz 668 669 # Method 2: Explicit pipe (most portable, works EVERYWHERE) 670 tar cf - /path/to/dir | xz -9e > archive.tar.xz 671 672 # Method 3: -I flag (GNU tar ONLY — Linux, not macOS) 673 tar -I 'xz -9e' -cf archive.tar.xz /path/to/dir 674 ``` 675 676 ### Portable Decompression (Explicit Pipe) 677 678 ```bash 679 # These work on any system regardless of tar implementation 680 xz -d < archive.tar.xz | tar xf - 681 zstd -d < archive.tar.zst | tar xf - 682 gzip -d < archive.tar.gz | tar xf - 683 ``` 684 685 ### Check Which tar You Have 686 687 ```bash 688 tar --version 689 # GNU tar 1.35 → you have GNU tar, -I works as compress program 690 # bsdtar 3.x.x → you have BSD tar, -I means --include, use pipes instead 691 ``` 692 693 > **Rule of thumb:** If your script needs to run on both Linux and macOS, always use explicit pipes (`tar cf - | compressor`) or `--use-compress-program`. Never rely on `-I`. 694 695 --- 696 697 ## Chained Workflows & Pipeline Recipes 698 699 The real power of tar comes from chaining it with other Unix tools via pipes. Since tar can write to stdout (`-f -`) and read from stdin (`-f -`), you can build arbitrarily complex pipelines: **compress → encrypt → split → checksum → transfer** — all in a single streaming operation with no intermediate files hitting disk. 700 701 ### The Pipeline Building Blocks 702 703 ``` 704 ┌──────┐ ┌────────────┐ ┌──────────┐ ┌───────┐ ┌──────────┐ 705 │ tar │───▶│ compressor │───▶│ encryptor│───▶│ split │───▶│ checksum │ 706 │ -cf -│ │ zstd/xz/gz │ │ gpg/age │ │ │ │ sha256 │ 707 └──────┘ └────────────┘ └──────────┘ └───────┘ └──────────┘ 708 ``` 709 710 Each block is optional. Mix and match depending on what you need. 711 712 --- 713 714 ### Compress + Split (Custom Levels) 715 716 When tar's built-in `-z`/`-J`/`--zstd` flags don't let you set a compression level, break the compressor out into its own pipe stage. 717 718 ```bash 719 # xz extreme + multi-threaded + split into 1GB parts 720 tar cf - /path/to/dir \\ 721 | xz -9e -T0 \\ 722 | split -b 1G -d -a 3 - archive.tar.xz.part- 723 724 # Reassemble and extract 725 cat archive.tar.xz.part-* | xz -d | tar xf - 726 727 # zstd level 19 + multi-threaded + split into 500MB parts 728 tar cf - /path/to/dir \\ 729 | zstd -19 -T0 \\ 730 | split -b 500M -d -a 3 - archive.tar.zst.part- 731 732 # Reassemble and extract 733 cat archive.tar.zst.part-* | zstd -d | tar xf - 734 735 # pigz (parallel gzip) level 9 + split into 100MB parts 736 tar cf - /path/to/dir \\ 737 | pigz -9 \\ 738 | split -b 100M -d - archive.tar.gz.part- 739 740 # Reassemble and extract 741 cat archive.tar.gz.part-* | pigz -d | tar xf - 742 ``` 743 744 --- 745 746 ### Compress + Encrypt + Split (The Full Chain) 747 748 The order matters: **always compress before encrypting**. Encrypted data is random and cannot be compressed further. 749 750 ```bash 751 # ── With GPG (symmetric) ── 752 tar cf - /path/to/dir \\ 753 | zstd -19 -T0 \\ 754 | gpg -c --cipher-algo AES256 --batch --passphrase-fd 3 3<<<'YourPassphrase' \\ 755 | split -b 500M -d -a 3 - archive.tar.zst.gpg.part- 756 757 # Reassemble, decrypt, decompress, extract 758 cat archive.tar.zst.gpg.part-* \\ 759 | gpg -d --batch --passphrase 'YourPassphrase' \\ 760 | zstd -d \\ 761 | tar xf - 762 763 # ── With GPG (asymmetric / public key) ── 764 tar cf - /path/to/dir \\ 765 | xz -9e -T0 \\ 766 | gpg -e -r recipient@example.com \\ 767 | split -b 1G -d -a 3 - archive.tar.xz.gpg.part- 768 769 # Recipient reassembles, decrypts, extracts 770 cat archive.tar.xz.gpg.part-* | gpg -d | xz -d | tar xf - 771 772 # ── With age (modern GPG alternative, simpler) ── 773 tar cf - /path/to/dir \\ 774 | zstd -19 -T0 \\ 775 | age -r age1ql3z7hjy54pw3hyww5ayyfg7zqgvc7w3j2elw8zmrj2kg5sfn9aqmcac8p \\ 776 > archive.tar.zst.age 777 778 # Decrypt and extract 779 age -d -i key.txt archive.tar.zst.age | zstd -d | tar xf - 780 781 # ── With age + split ── 782 tar cf - /path/to/dir \\ 783 | zstd -19 -T0 \\ 784 | age -r age1ql3z7hjy54pw3hyww5ayyfg7zqgvc7w3j2elw8zmrj2kg5sfn9aqmcac8p \\ 785 | split -b 500M -d -a 3 - archive.tar.zst.age.part- 786 787 cat archive.tar.zst.age.part-* | age -d -i key.txt | zstd -d | tar xf - 788 789 # ── With OpenSSL ── 790 tar cf - /path/to/dir \\ 791 | xz -9e -T0 \\ 792 | openssl enc -aes-256-cbc -salt -pbkdf2 \\ 793 | split -b 500M -d -a 3 - archive.tar.xz.enc.part- 794 795 cat archive.tar.xz.enc.part-* \\ 796 | openssl enc -d -aes-256-cbc -pbkdf2 \\ 797 | xz -d \\ 798 | tar xf - 799 ``` 800 801 --- 802 803 ### Compress + Split + Checksum (Integrity Verification) 804 805 Generate checksums for each split part so you can verify after transfer. 806 807 ```bash 808 # Create, compress, split, then checksum 809 tar cf - /path/to/dir | zstd -19 -T0 | split -b 500M -d -a 3 - archive.tar.zst.part- 810 sha256sum archive.tar.zst.part-* > archive.tar.zst.sha256 811 812 # After transfer, verify 813 sha256sum -c archive.tar.zst.sha256 814 815 # Or inline: tee into sha256sum while splitting 816 tar cf - /path/to/dir \\ 817 | zstd -19 -T0 \\ 818 | tee >(sha256sum > archive-whole.sha256) \\ 819 | split -b 500M -d -a 3 - archive.tar.zst.part- 820 ``` 821 822 --- 823 824 ### Compress + Encrypt + Split + Checksum (Full Paranoia Pipeline) 825 826 ```bash 827 # ── CREATE ── 828 tar cf - /path/to/dir \\ 829 | zstd -19 -T0 \\ 830 | gpg -c --cipher-algo AES256 \\ 831 | split -b 500M -d -a 3 - archive.tar.zst.gpg.part- 832 833 # Checksum all parts 834 sha256sum archive.tar.zst.gpg.part-* > checksums.sha256 835 836 # ── VERIFY + RESTORE ── 837 sha256sum -c checksums.sha256 && \\ 838 cat archive.tar.zst.gpg.part-* | gpg -d | zstd -d | tar xf - -C /restore/ 839 ``` 840 841 --- 842 843 ### Compress + Progress Bar + Split 844 845 Use `pv` (pipe viewer) to monitor progress at any stage of the pipeline. 846 847 ```bash 848 # Show progress while compressing and splitting 849 tar cf - /path/to/dir \\ 850 | pv -s $(du -sb /path/to/dir | cut -f1) -N "tar" \\ 851 | zstd -19 -T0 \\ 852 | pv -N "zstd" \\ 853 | split -b 500M -d -a 3 - archive.tar.zst.part- 854 855 # Show progress while reassembling and extracting 856 cat archive.tar.zst.part-* \\ 857 | pv -N "reassemble" \\ 858 | zstd -d \\ 859 | tar xf - -C /restore/ 860 861 # Progress bar with encryption 862 tar cf - /path/to/dir \\ 863 | pv -s $(du -sb /path/to/dir | cut -f1) \\ 864 | zstd -19 -T0 \\ 865 | gpg -c --cipher-algo AES256 \\ 866 > archive.tar.zst.gpg 867 868 # pv -W (wait) is useful when piping into gpg since gpg prompts for a 869 # passphrase before processing — -W delays the progress bar until data flows 870 tar cf - /path/to/dir \\ 871 | zstd -19 -T0 \\ 872 | pv -W \\ 873 | gpg -c --cipher-algo AES256 \\ 874 > archive.tar.zst.gpg 875 ``` 876 877 --- 878 879 ### Compress + Network Transfer (SSH) 880 881 Stream directly to a remote host — nothing touches local disk except the source. 882 883 ```bash 884 # ── Push: local → remote (zstd, multi-threaded) ── 885 tar cf - /path/to/dir \\ 886 | zstd -19 -T0 \\ 887 | ssh user@remote 'zstd -d | tar xf - -C /opt/deploy/' 888 889 # ── Push: local → remote (save as file on remote) ── 890 tar cf - /path/to/dir \\ 891 | zstd -19 -T0 \\ 892 | ssh user@remote 'cat > /backup/archive.tar.zst' 893 894 # ── Pull: remote → local ── 895 ssh user@remote 'tar cf - /remote/dir | zstd -T0' \\ 896 | zstd -d \\ 897 | tar xf - -C /local/restore/ 898 899 # ── Push with progress ── 900 tar cf - /path/to/dir \\ 901 | pv -s $(du -sb /path/to/dir | cut -f1) \\ 902 | zstd -T0 \\ 903 | ssh user@remote 'zstd -d | tar xf - -C /opt/deploy/' 904 905 # ── Clone directory between hosts (one-liner) ── 906 ssh user@source 'tar cf - /data | zstd -T0' \\ 907 | ssh user@dest 'zstd -d | tar xf - -C /' 908 ``` 909 910 --- 911 912 ### Compress + Network Transfer (Netcat — No SSH Overhead) 913 914 Fastest possible transfer on a trusted LAN. No encryption, no SSH overhead. 915 916 ```bash 917 # ── Receiver (start first) ── 918 nc -l -p 9000 | zstd -d | tar xf - -C /restore/ 919 920 # ── Sender ── 921 tar cf - /path/to/dir | zstd -T0 | nc receiver-host 9000 922 923 # ── With progress on sender side ── 924 tar cf - /path/to/dir \\ 925 | pv -s $(du -sb /path/to/dir | cut -f1) \\ 926 | zstd -T0 \\ 927 | nc receiver-host 9000 928 929 # ── With inline checksum verification ── 930 # Sender (prints md5 to stderr after transfer) 931 tar cf - /path/to/dir | zstd -T0 | tee >(md5sum >&2) | nc receiver-host 9000 932 933 # Receiver (prints md5 to stderr after receiving) 934 nc -l -p 9000 | tee >(md5sum >&2) | zstd -d | tar xf - -C /restore/ 935 # Compare the two md5 hashes — they should match 936 ``` 937 938 --- 939 940 ### Compress + Encrypt + Network Transfer 941 942 ```bash 943 # ── Push encrypted archive over SSH ── 944 tar cf - /path/to/dir \\ 945 | zstd -19 -T0 \\ 946 | gpg -c --cipher-algo AES256 \\ 947 | ssh user@remote 'cat > /backup/archive.tar.zst.gpg' 948 949 # ── Pull, decrypt, extract in one shot ── 950 ssh user@remote 'cat /backup/archive.tar.zst.gpg' \\ 951 | gpg -d \\ 952 | zstd -d \\ 953 | tar xf - -C /local/restore/ 954 955 # ── Netcat + encryption (for untrusted networks without SSH) ── 956 # Receiver: 957 nc -l -p 9000 | gpg -d | zstd -d | tar xf - -C /restore/ 958 959 # Sender: 960 tar cf - /path/to/dir | zstd -T0 | gpg -c --cipher-algo AES256 | nc receiver-host 9000 961 ``` 962 963 --- 964 965 ### Incremental Backup + Compress + Encrypt + Split 966 967 Full automated backup pipeline with incrementals. 968 969 ```bash 970 SNAP="/backup/snapshot.snar" 971 DATE=$(date +%F) 972 973 # ── Full backup (first run or when snapshot is deleted) ── 974 tar -g "$SNAP" -cf - /home/user \\ 975 | zstd -19 -T0 \\ 976 | gpg -c --cipher-algo AES256 \\ 977 | split -b 1G -d -a 3 - "/backup/full-${DATE}.tar.zst.gpg.part-" 978 sha256sum /backup/full-${DATE}.tar.zst.gpg.part-* > "/backup/full-${DATE}.sha256" 979 980 # ── Incremental backup (subsequent runs) ── 981 tar -g "$SNAP" -cf - /home/user \\ 982 | zstd -12 -T0 \\ 983 | gpg -c --cipher-algo AES256 \\ 984 > "/backup/inc-${DATE}.tar.zst.gpg" 985 sha256sum "/backup/inc-${DATE}.tar.zst.gpg" >> "/backup/inc-${DATE}.sha256" 986 987 # ── Restore: full first, then each incremental in order ── 988 sha256sum -c /backup/full-2026-03-01.sha256 && \\ 989 cat /backup/full-2026-03-01.tar.zst.gpg.part-* \\ 990 | gpg -d | zstd -d | tar xf - -g /dev/null -C /restore/ 991 992 gpg -d /backup/inc-2026-03-02.tar.zst.gpg \\ 993 | zstd -d | tar xf - -g /dev/null -C /restore/ 994 ``` 995 996 --- 997 998 ### Exclude + Find + Compress + Encrypt (Surgical Archives) 999 1000 ```bash 1001 # Archive only files modified in last 7 days, compress with zstd, encrypt with age 1002 find /project -mtime -7 -type f -print0 \\ 1003 | tar cf - --null -T - \\ 1004 | zstd -19 -T0 \\ 1005 | age -r age1ql3z7hjy54pw3hyww5ayyfg7zqgvc7w3j2elw8zmrj2kg5sfn9aqmcac8p \\ 1006 > recent-changes.tar.zst.age 1007 1008 # Archive specific file types, exclude build artifacts, compress + split 1009 find /project \begin{raycast-math} -name '*.py' -o -name '*.rs' -o -name '*.toml' \end{raycast-math} -print0 \\ 1010 | tar cf - --null -T - \\ 1011 --exclude='target' \\ 1012 --exclude='__pycache__' \\ 1013 | zstd -19 -T0 \\ 1014 | split -b 100M -d -a 3 - source-code.tar.zst.part- 1015 ``` 1016 1017 --- 1018 1019 ### Extract to Pipe (Process Each File) 1020 1021 Use `--to-command` to pipe each extracted file into a program instead of writing to disk. 1022 1023 ```bash 1024 # Pipe every extracted file through a processor (e.g. wc -l to count lines) 1025 tar xf archive.tar.gz --to-command='wc -l' 1026 1027 # The filename is available inside --to-command as $TAR_FILENAME 1028 tar xf archive.tar.gz --to-command='echo "Processing: $TAR_FILENAME"' 1029 1030 # Extract and pipe each file into a script 1031 tar xf archive.tar.gz --to-command='/path/to/your/script.sh' 1032 ``` 1033 1034 --- 1035 1036 ### Copy Directory Trees (Local Cloning) 1037 1038 The fastest way to copy a directory preserving all metadata — faster than `cp -a` or `rsync` for local copies. 1039 1040 ```bash 1041 # Clone a directory tree preserving permissions, ownership, timestamps 1042 tar cf - -C /source/dir . | tar xpf - -C /dest/dir 1043 1044 # Same but with progress 1045 tar cf - -C /source/dir . \\ 1046 | pv -s $(du -sb /source/dir | cut -f1) \\ 1047 | tar xpf - -C /dest/dir 1048 1049 # Clone with full metadata preservation 1050 tar cf - --acls --xattrs --selinux -C /source/dir . \\ 1051 | tar xpf - --acls --xattrs --selinux -C /dest/dir 1052 ``` 1053 1054 --- 1055 1056 ### Quick Reference: Pipeline Order 1057 1058 When combining operations, follow this order: 1059 1060 ``` 1061 CREATE → COMPRESS → ENCRYPT → SPLIT → CHECKSUM → TRANSFER 1062 tar → zstd/xz → gpg/age → split → sha256 → ssh/nc 1063 ``` 1064 1065 And to reverse: 1066 1067 ``` 1068 REASSEMBLE → VERIFY → DECRYPT → DECOMPRESS → EXTRACT 1069 cat → sha256sum → gpg/age → zstd/xz → tar 1070 ``` 1071 1072 > **Key principle:** Every tool in the chain reads from stdin and writes to stdout. The pipe (`|`) connects them. Use `-f -` to tell tar to read/write stdin/stdout instead of a file. 1073 1074 --- 1075 1076 ### Encryption Tool Comparison for Pipelines 1077 1078 | Tool | Type | Pipe-friendly | Key Management | Notes | 1079 |------|------|:---:|---|---| 1080 | `gpg -c` | Symmetric (passphrase) | ✅ | None needed | Universal, prompts for passphrase | 1081 | `gpg -e -r` | Asymmetric (public key) | ✅ | Keyring required | Standard for sharing with others | 1082 | `age -p` | Symmetric (passphrase) | ✅ | None needed | Modern, simple, no config | 1083 | `age -r` | Asymmetric (public key) | ✅ | Single key file | No keyring, just a file | 1084 | `openssl enc` | Symmetric (passphrase) | ✅ | None needed | Always available, more flags | 1085 1086 Install `age`: `brew install age` / `sudo apt install age` / `sudo pacman -S age`