I really like @LuizAngeloDarosdeLuca's solution because of its simplicity and because it is completely based on bash!
In my experience, however, gzip often fails to recognize a few trailing 'garbage' bytes as such. In these cases, it instead falsely signals a premature end of the archive. If even just one more byte is then included as belonging to the archive, gzip immediately comes to the (correct) conclusion that there is trailing garbage. The algorithm presented by Luiz is thus in an endless loop in which $size no longer changes, although the correct size of the archive has not yet been found and will never be found.
One solution can be to check at the end of each iteration of the while loop whether $size has changed at all, compared to its previous value. If this is not the case, the loop is aborted and instead, starting from the last value of $min, $size is decremented in 1-byte steps until gzip confirms the correct size or the archive.
The complete algorithm then looks like this:
#!/bin/bash
set -e
gzip=${1:?Inform a gzip file}
size=$(stat -c%s "$gzip")
size_previous=0
min=0
max=$size
while true; do
if head -c "$size" "$gzip" | gzip -v -t - &>/dev/null; then
echo $size
exit
else
case "$?" in
1) min=$size ;;
2) max=$size ;;
esac
if (( size == size_previous )); then
break
else
size_previous=$size
fi
fi
done
for (( size = min; size > 0; size-- )); do
if head -c $size "$gzip" | gzip -t - &> /dev/null; then
echo $size
exit
fi
done