Created
June 18, 2026 18:40
-
-
Save nyteshade/7c9f9cde7daf23c840a156e83abb7a73 to your computer and use it in GitHub Desktop.
unpkg
This file contains hidden or 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/zsh | |
| setopt pipefail | |
| showUsage() { | |
| local tool="\x1b[1;35munpkg\x1b[22;39m" | |
| printf "${tool} <path-to-package.pkg> <destination-directory>\n" | |
| printf "${tool} --to-zip <path-to-package.pkg> [destination.zip]\n" | |
| printf "${tool} -z <path-to-package.pkg> [destination.zip]\n" | |
| printf "${tool} --to-tar-gzip <path-to-package.pkg> [destination.tar.gz]\n" | |
| printf "${tool} -tgz <path-to-package.pkg> [destination.tar.gz]\n\n" | |
| printf "${tool} extracts macOS .pkg archives into contents/ and scripts/.\n" | |
| printf "Payload and Scripts archives may be gzip-compressed cpio or plain cpio.\n" | |
| } | |
| missingFile() { | |
| printf "The file specified ${1}, cannot be found.\n" | |
| } | |
| requireCommand() { | |
| if ! command -v "${1}" >/dev/null 2>&1; then | |
| printf "Required command '${1}' was not found.\n" | |
| cleanup 1 | |
| fi | |
| } | |
| cleanup() { | |
| local exitStatus="${1:-$?}" | |
| trap - EXIT INT TERM | |
| cd "${curDir}" 2>/dev/null | |
| if [[ -n "${tmpDir}" && -d "${tmpDir}" ]]; then | |
| rm -rf "${tmpDir}" | |
| if ! [[ "${?}" -eq 0 ]]; then | |
| printf "Could not remove temp directory '${tmpDir}'\n" | |
| exit 1 | |
| fi | |
| fi | |
| exit "${exitStatus}" | |
| } | |
| extractCpioArchive() { | |
| local archive="${1}" | |
| local outDir="${2}" | |
| local label="${3:-archive}" | |
| mkdir -p "${outDir}" | |
| if ! [[ "${?}" -eq 0 ]]; then | |
| printf "Could not create output directory ${outDir}\n" | |
| cleanup 1 | |
| fi | |
| cd "${outDir}" | |
| if ! [[ "${?}" -eq 0 ]]; then | |
| printf "Could not enter output directory ${outDir}\n" | |
| cleanup 1 | |
| fi | |
| if gzip -t "${archive}" >/dev/null 2>&1; then | |
| gzip -dc "${archive}" | cpio -idm | |
| else | |
| cpio -idm < "${archive}" | |
| fi | |
| if ! [[ "${?}" -eq 0 ]]; then | |
| printf "Could not extract ${label} archive ${archive}\n" | |
| cleanup 1 | |
| fi | |
| } | |
| installRootForPayload() { | |
| local payload="${1}" | |
| local contentsRoot="${2}" | |
| local packageInfo="$(dirname "${payload}")/PackageInfo" | |
| local installLocation="/" | |
| local relativeLocation="" | |
| if [[ -f "${packageInfo}" ]]; then | |
| installLocation="$(sed -n 's/.*install-location="\([^"]*\)".*/\1/p' "${packageInfo}" | head -n 1)" | |
| fi | |
| if [[ -z "${installLocation}" || "${installLocation}" == "/" ]]; then | |
| printf "%s" "${contentsRoot}" | |
| return | |
| fi | |
| relativeLocation="${installLocation#/}" | |
| if [[ "${relativeLocation}" == *".."* ]]; then | |
| printf "Refusing unsafe install-location '${installLocation}' in ${packageInfo}\n" >&2 | |
| cleanup 1 | |
| fi | |
| printf "%s/%s" "${contentsRoot}" "${relativeLocation}" | |
| } | |
| makeZip() { | |
| local sourceDir="${1}" | |
| local zipFile="${2}" | |
| local zipParent="$(dirname "${zipFile}")" | |
| local zipName="$(basename "${zipFile}")" | |
| mkdir -p "${zipParent}" | |
| if ! [[ "${?}" -eq 0 ]]; then | |
| printf "Could not create zip output directory ${zipParent}\n" | |
| cleanup 1 | |
| fi | |
| zipFile="$(cd "${zipParent}" && pwd)/${zipName}" | |
| rm -f "${zipFile}" | |
| cd "${sourceDir}" | |
| if ! [[ "${?}" -eq 0 ]]; then | |
| printf "Could not enter staging directory ${sourceDir}\n" | |
| cleanup 1 | |
| fi | |
| zip -qry "${zipFile}" . | |
| if ! [[ "${?}" -eq 0 ]]; then | |
| printf "Could not create zip file ${zipFile}\n" | |
| cleanup 1 | |
| fi | |
| } | |
| makeTarGzip() { | |
| local sourceDir="${1}" | |
| local tarFile="${2}" | |
| local tarParent="$(dirname "${tarFile}")" | |
| local tarName="$(basename "${tarFile}")" | |
| mkdir -p "${tarParent}" | |
| if ! [[ "${?}" -eq 0 ]]; then | |
| printf "Could not create tar output directory ${tarParent}\n" | |
| cleanup 1 | |
| fi | |
| tarFile="$(cd "${tarParent}" && pwd)/${tarName}" | |
| rm -f "${tarFile}" | |
| cd "${sourceDir}" | |
| if ! [[ "${?}" -eq 0 ]]; then | |
| printf "Could not enter staging directory ${sourceDir}\n" | |
| cleanup 1 | |
| fi | |
| tar -czf "${tarFile}" . | |
| if ! [[ "${?}" -eq 0 ]]; then | |
| printf "Could not create tar gzip file ${tarFile}\n" | |
| cleanup 1 | |
| fi | |
| } | |
| defaultArchiveDestination() { | |
| local sourceFile="${1}" | |
| local extension="${2}" | |
| local sourceDir="$(dirname "${sourceFile}")" | |
| local sourceName="$(basename "${sourceFile}")" | |
| local baseName="${sourceName%.*}" | |
| printf "%s/%s%s" "${sourceDir}" "${baseName}" "${extension}" | |
| } | |
| ensureArchiveExtension() { | |
| local outputFile="${1}" | |
| local extension="${2}" | |
| local alternateExtension="${3}" | |
| local lowerOutput="${(L)outputFile}" | |
| if [[ "${lowerOutput}" == *"${extension}" || ( -n "${alternateExtension}" && "${lowerOutput}" == *"${alternateExtension}" ) ]]; then | |
| printf "%s" "${outputFile}" | |
| else | |
| printf "%s%s" "${outputFile}" "${extension}" | |
| fi | |
| } | |
| local archiveMode="" | |
| if [[ "${1}" == "--to-zip" || "${1}" == "-z" ]]; then | |
| archiveMode="zip" | |
| shift | |
| elif [[ "${1}" == "--to-tar-gzip" || "${1}" == "-tgz" ]]; then | |
| archiveMode="tgz" | |
| shift | |
| fi | |
| if [[ -n "${archiveMode}" && ( "${#}" -lt 1 || "${#}" -gt 2 ) ]]; then | |
| showUsage | |
| exit 0 | |
| fi | |
| if [[ -z "${archiveMode}" && "${#}" -ne 2 ]]; then | |
| showUsage | |
| exit 0 | |
| fi | |
| if ! [[ -f "${1}" ]]; then | |
| missingFile "${1}" | |
| exit 1 | |
| fi | |
| requireCommand xar | |
| requireCommand gzip | |
| requireCommand cpio | |
| if [[ "${archiveMode}" == "zip" ]]; then | |
| requireCommand zip | |
| elif [[ "${archiveMode}" == "tgz" ]]; then | |
| requireCommand tar | |
| fi | |
| local file="$(cd "$(dirname "${1}")" && pwd)/$(basename "${1}")" | |
| local curDir="$(pwd)" | |
| local destination="${2}" | |
| local tmpDir="$(mktemp -d /tmp/unpkg.XXXXXXXX)" | |
| local pkgDir="${tmpDir}/pkg" | |
| local extractDir="${destination}" | |
| local contentsDir="${destination}/contents" | |
| local scriptsDir="${destination}/scripts" | |
| if [[ "${archiveMode}" == "zip" ]]; then | |
| if [[ -z "${destination}" ]]; then | |
| destination="$(defaultArchiveDestination "${file}" ".zip")" | |
| else | |
| destination="$(ensureArchiveExtension "${destination}" ".zip")" | |
| fi | |
| elif [[ "${archiveMode}" == "tgz" ]]; then | |
| if [[ -z "${destination}" ]]; then | |
| destination="$(defaultArchiveDestination "${file}" ".tar.gz")" | |
| else | |
| destination="$(ensureArchiveExtension "${destination}" ".tar.gz" ".tgz")" | |
| fi | |
| fi | |
| if [[ "${destination}" != /* ]]; then | |
| destination="${curDir}/${destination}" | |
| fi | |
| extractDir="${destination}" | |
| contentsDir="${destination}/contents" | |
| scriptsDir="${destination}/scripts" | |
| if ! [[ -d "${tmpDir}" ]]; then | |
| printf "Could not create temporary directory.\n" | |
| exit 1 | |
| fi | |
| trap 'cleanup $?' EXIT INT TERM | |
| mkdir -p "${pkgDir}" | |
| if ! [[ "${?}" -eq 0 ]]; then | |
| printf "Could not create temporary package directory ${pkgDir}\n" | |
| cleanup 1 | |
| fi | |
| cd "${pkgDir}" | |
| if ! [[ "${?}" -eq 0 ]]; then | |
| printf "Could not enter temporary package directory ${pkgDir}\n" | |
| cleanup 1 | |
| fi | |
| xar -xf "${file}" | |
| if ! [[ "${?}" -eq 0 ]]; then | |
| printf "Could not extract package ${file}\n" | |
| cleanup 1 | |
| fi | |
| local -a payloads rawPayloads | |
| rawPayloads=( "${(@f)$(find "${pkgDir}" -type f -name 'Payload' -print)}" ) | |
| payloads=() | |
| for payloadPath in "${rawPayloads[@]}"; do | |
| if [[ -n "${payloadPath}" ]]; then | |
| payloads+=( "${payloadPath}" ) | |
| fi | |
| done | |
| local -a scripts rawScripts | |
| rawScripts=( "${(@f)$(find "${pkgDir}" -type f -name 'Scripts' -print)}" ) | |
| scripts=() | |
| for scriptPath in "${rawScripts[@]}"; do | |
| if [[ -n "${scriptPath}" ]]; then | |
| scripts+=( "${scriptPath}" ) | |
| fi | |
| done | |
| if [[ "${#payloads}" -eq 0 ]]; then | |
| printf "Could not find a file called 'Payload' in the extracted contents.\n" | |
| printf "Find the file, move to your target directory, then run one of:\n" | |
| printf "gzip -dc ${tmpDir}/path/to/Payload | cpio -idm\n" | |
| printf "cpio -idm < ${tmpDir}/path/to/Payload\n" | |
| cleanup 1 | |
| fi | |
| if [[ -n "${archiveMode}" ]]; then | |
| extractDir="${tmpDir}/contents" | |
| contentsDir="${extractDir}/contents" | |
| scriptsDir="${extractDir}/scripts" | |
| fi | |
| mkdir -p "${contentsDir}" "${scriptsDir}" | |
| if ! [[ "${?}" -eq 0 ]]; then | |
| printf "Could not create output directories under ${extractDir}\n" | |
| cleanup 1 | |
| fi | |
| for payload in "${payloads[@]}"; do | |
| extractCpioArchive "${payload}" "$(installRootForPayload "${payload}" "${contentsDir}")" "Payload" | |
| done | |
| for scriptArchive in "${scripts[@]}"; do | |
| extractCpioArchive "${scriptArchive}" "${scriptsDir}" "Scripts" | |
| done | |
| if [[ "${archiveMode}" == "zip" ]]; then | |
| makeZip "${extractDir}" "${destination}" | |
| elif [[ "${archiveMode}" == "tgz" ]]; then | |
| makeTarGzip "${extractDir}" "${destination}" | |
| fi | |
| cleanup 0 |
Author
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I generally rename this to
unpkgand place it in my ~/.local/bin, but this has helped me get the files and scripts out of an Apple .pkg file without executing it. This can be important in constrained environments.It's tested on my local needs thus far, your mileage may vary.