无需提取.pkg获取其中.app版本(macOS、Node.js及跨平台)
1. Can I get the .app version from a .pkg file on macOS using Node.js without extracting the entire package?
Absolutely! macOS .pkg files are essentially xar archives that contain a compressed Payload (a gzipped cpio archive) holding the actual app bundle. You can parse this structure directly in Node.js without fully extracting the pkg by using libraries that handle xar, cpio, gzip, and plist parsing.
Here's a practical example:
First, install the necessary dependencies:
npm install xar plist zlib cpioThen write a script to parse the pkg:
const xar = require('xar'); const plist = require('plist'); const zlib = require('zlib'); const cpio = require('cpio'); const fs = require('fs'); async function getAppVersionFromPkg(pkgPath, appBundleName) { // Parse the xar archive const archive = await xar.open(pkgPath); const payloadEntry = archive.files.find(f => f.path === 'Payload'); if (!payloadEntry) throw new Error('No Payload found in the pkg'); // Extract and decompress the Payload (gzipped cpio) const payloadBuffer = await payloadEntry.extract(); const cpioBuffer = await new Promise((resolve, reject) => { zlib.gunzip(payloadBuffer, (err, data) => err ? reject(err) : resolve(data)); }); // Locate the app's Info.plist in the cpio archive const cpioEntries = cpio.parse(cpioBuffer); const infoPlistEntry = cpioEntries.find(entry => entry.name.includes(`${appBundleName}/Contents/Info.plist`) ); if (!infoPlistEntry) throw new Error('Info.plist not found for the target app'); // Parse the plist to pull version data const infoPlist = plist.parse(infoPlistEntry.data); return { shortVersion: infoPlist.CFBundleShortVersionString, bundleVersion: infoPlist.CFBundleVersion }; } // Usage example getAppVersionFromPkg('./MyApp.pkg', 'MyApp.app') .then(version => console.log('App versions:', version)) .catch(err => console.error('Error:', err));
Note: A small number of pkg files might have nested structures or encrypted sections (rare for standard app distribution pkgs), so you may need to tweak the script for edge cases.
2. Can this be done with other programming languages on other OSes (e.g., Windows)?
Yes, definitely! The .pkg format relies on open standards (xar, cpio, gzip, plist), so you can replicate this workflow on any operating system as long as you have access to libraries that handle these formats.
Example for Windows with Python:
Python has cross-platform libraries to cover all these steps:
Install dependencies:
pip install xarfile pycpio plistlibA simplified script outline:
import xarfile import gzip import pycpio import plistlib def get_app_version(pkg_path, app_bundle_name): with xarfile.open(pkg_path) as xar: payload = xar.getmember('Payload') with xar.open(payload) as payload_file: # Decompress the gzipped payload with gzip.GzipFile(fileobj=payload_file) as gz: cpio_data = gz.read() # Parse the cpio archive cpio_archive = pycpio.CPIOArchive(cpio_data) # Locate the app's Info.plist info_plist_path = f"{app_bundle_name}/Contents/Info.plist" for entry in cpio_archive: if entry.name == info_plist_path: plist_data = cpio_archive.read(entry) info_plist = plistlib.loads(plist_data) return { "short_version": info_plist.get("CFBundleShortVersionString"), "bundle_version": info_plist.get("CFBundleVersion") } return None # Usage example version = get_app_version("C:/Downloads/MyApp.pkg", "MyApp.app") print(version)
If you prefer command-line tools on Windows, you could use third-party xar extractors to pull out the Payload, decompress it with a gzip tool, extract the Info.plist via a cpio utility, then parse the plist with a dedicated viewer. That said, using a programming language gives you more automation and control without manual steps.
The core idea is simple: as long as you can parse the underlying archive formats and plist files, you can extract the app version no matter which OS you're working on.
内容的提问来源于stack exchange,提问作者splash27

