Created
February 23, 2024 09:04
-
-
Save jow-/3cfbdb19342c2ebcdeaa4a4daef42433 to your computer and use it in GitHub Desktop.
Generate a .tar.gz archive with shell
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/sh | |
make_tar_member() { | |
local name=$1 | |
local content=$2 | |
local mode=644 | |
local uid=1000 | |
local gid=1000 | |
local size=${#content} | |
local mtime=$(date +%s) | |
local type=0 | |
local link="" | |
local username="jow" | |
local groupname="jow" | |
# 100 padding bytes, using 0x01 since the shell does not tolerate null bytes in strings | |
local pad=$'\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1' | |
# truncate string header values to their maximum length | |
name=${name:0:100} | |
link=${link:0:100} | |
username=${username:0:32} | |
groupname=${groupname:0:32} | |
# construct header part before checksum field | |
local header1="${name}${pad:0:$((100 - ${#name}))}" | |
header1="${header1}$(printf '%07d\1' $mode)" | |
header1="${header1}$(printf '%07o\1' $uid)" | |
header1="${header1}$(printf '%07o\1' $gid)" | |
header1="${header1}$(printf '%011o\1' $size)" | |
header1="${header1}$(printf '%011o\1' $mtime)" | |
# construct header part after checksum field | |
local header2="$(printf '%d' $type)" | |
header2="${header2}${link}${pad:0:$((100 - ${#link}))}" | |
header2="${header2}ustar ${pad:0:1}" | |
header2="${header2}${username}${pad:0:$((32 - ${#username}))}" | |
header2="${header2}${groupname}${pad:0:$((32 - ${#groupname}))}" | |
# calculate checksum over header fields | |
local checksum=0 | |
for byte in $(printf '%s%8s%s' "$header1" "" "$header2" | tr '\1' '\0' | hexdump -ve '1/1 "%u "'); do | |
checksum=$((checksum + byte)) | |
done | |
# print member header, padded to 512 byte | |
printf '%s%06o\0 %s' "$header1" $checksum "$header2" | tr '\1' '\0' | |
dd if=/dev/zero bs=183 count=1 2>/dev/null | |
# print content data, padded to multiple of 512 byte | |
printf "%s" "$content" | |
dd if=/dev/zero bs=1 count=$((512 - (size % 512))) 2>/dev/null | |
} | |
{ | |
make_tar_member "example.txt" "Hello there" | |
make_tar_member "example-2.txt" "Blah blah" | |
# alternatively, instead of dd'ing the trailing padding, run a `tar -c ...` here | |
dd if=/dev/zero bs=1024 count=1 2>/dev/null | |
} | gzip > /tmp/test.tar.gz | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment