unpackΒΆ

#!/bin/sh -e
#
# Unpack a tar archive and print the top-level directory in the archive.
# Specifically, print the second line of the archive list minus a leading '/'.

usage="
Usage: $0 [options] filename

Unpack a tar archive and print the top-level directory in the archive.

Optional flags:
  -d           Dry run. Don't unpack the archive, just print the top-level directory.
  -t           Unpack archive to a temporary directory.
  -T TEMPLATE  Like -t, but use the given template for the temporary directory.
  -v           Verbose. Run tar with the verbose flag. Ignored when used with -d.
"

u_realpath() {
    set +e
    curdir="$(pwd)"
    link="$1"
    while [ "$link" ] ; do
        dir_name="${link%/*}"
        [ -d "$dir_name" ] && cd "$dir_name"
        lastlink=$link
        link="$(readlink "${link##*/}")"
    done
    printf '%s\n' "$(pwd)/${lastlink##*/}"
    cd "$curdir"
    set -e
}

error() {
    printf '%s\n' "$@"
    exit 1
}

transform_tar_output() {
    head -n2 | awk '{if ((FNR % 2) == 0) {print $1}}' | sed 's@/.*@@'
}

dry_run=0
make_tmp_dir=0
verbose=''
template=''
while getopts dtT:v arg ; do
    case $arg in
        d) dry_run=1;;
        t) make_tmp_dir=1;;
        T) make_tmp_dir=1; template="-t $2";;
        v) verbose='-v';;
        *) printf 'Unsupported argument %s\n' "$arg" && exit 1;;
    esac
done
shift $((OPTIND - 1))

[ -n "$1" ] || error "$usage"
[ -e "$1" ] || error "No such file: $1"

archive="$(u_realpath "$1")"
tmp_dir=''

# Prefer the file command for guessing file type, if it is available.
if file --version >/dev/null ; then
    fileout="$(file "$archive")"
    ext=$(printf '%s' "${fileout#*:}" | awk '{print $1}')
else
    ext=${archive##*.}
fi

case $ext in
    bzip2|bz2|BZ2)  tool='bzcat';;
    gzip|tgz|gz|GZ) tool='zcat';;
    xz|XZ)          tool='xzcat';;
    *)              tool='tar';;
esac

if [ $make_tmp_dir = 1 ] ; then
    # shellcheck disable=SC2086
    tmp_dir="$(mktemp -d $template)/"
    cd "$tmp_dir"
fi

case $tool in
    *zcat)
        top_dir="${tmp_dir}$($tool "$archive" | tar -tf - | transform_tar_output)"
        [ $dry_run -eq 1 ] || $tool "$archive" 2>/dev/null | tar $verbose -xf - 2>/dev/null
        ;;
    tar)
        top_dir="${tmp_dir}$(tar -tf "$archive" | transform_tar_output)"
        [ $dry_run -eq 1 ] || tar $verbose -xf "$archive" 2>/dev/null
        ;;
esac

if [ $dry_run -eq 1 ] && [ $make_tmp_dir -eq 1 ] && [ -d "$tmp_dir" ] ; then
    rm -rf "$tmp_dir"
fi

printf '%s\n' "$top_dir"