2

I'm writing an auto update facility for my cross platform application. The updater portion downloads the installer file and executes a shell command to install it. On MacOS our "installer" takes the form of .dmg file. I need to be able to silently mount the disk image, copy/overwrite the contained .app(s) to the destination directory, then unmount the disk image. I am assuming the disk image contains a bundle that can be directly copied to /Applications or elsewhere. There is no sensible way to handle an arbitrary .dmg file as asked before, as its contents cannot be known. Some assumptions must be made.

Tim Angus
  • 983
  • 11
  • 26
  • Possible cross-site duplicate: https://apple.stackexchange.com/questions/73926/is-there-a-command-to-install-a-dmg – tripleee Apr 26 '19 at 14:49
  • Possible duplicate of [Install dmg package on MAC OS from Terminal](https://stackoverflow.com/questions/22934083/install-dmg-package-on-mac-os-from-terminal) – tripleee Apr 26 '19 at 14:54
  • No, that is a different (and bad) question. That is asking how to install a dmg from the command line. A dmg is not something that /can/ be installed, it's simply a disk image, like an .iso. I'm specifically wanting to install a .app that is contained within a .dmg. The stackexchange question has some slightly better /answers/, but the question itself is also not good, for the same reasons as above. – Tim Angus Apr 26 '19 at 15:10

1 Answers1

4
VOLUME=$(hdiutil attach -nobrowse '[DMG FILE]' |
    tail -n1 | cut -f3-; exit ${PIPESTATUS[0]}) &&
(rsync -a "$VOLUME"/*.app /Applications/; SYNCED=$?
    (hdiutil detach -force -quiet "$VOLUME" || exit $?) && exit "$SYNCED")

I'll break this down:

  • hdiutil attach -nobrowse '[DMG FILE]' Mount the disk image, but don't show it on the desktop
  • | tail -n1 | cut -f3- Discard the first two tokens of hdiutil's last line output, leaving the remainder, which is the mounted volume
  • VOLUME=$(...; exit ${PIPESTATUS[0]}) Set VOLUME to the output of the above, and set the exit code to that of hdiutil
  • && If the disk image was mounted successfully...
  • rsync -a "$VOLUME"/*.app /Applications/ ...use rsync to copy the .app files to the /Applications directory, while preserving permissions/symlinks/ownership etc.
  • ; SYNCED=$? Store result of rsync operation
  • (hdiutil detach -force -quiet "$VOLUME" force unmount the disk image
  • || exit $?) && "$SYNCED" Exit with hdiutil exit code, or rsync exit code if hdiutil succeeded
Tim Angus
  • 983
  • 11
  • 26
  • 1
    Putting this in double quotes looks wrong to me; the *calling* shell will expand `$VOLUME` before `bash -c` starts running. (Why run this in a separate Bash instance anyway?) – tripleee Apr 26 '19 at 14:34
  • Also, don't use uppercase for your private variables. – tripleee Apr 26 '19 at 14:35
  • Because if I just run it naked, it will use whatever the user's shell is set to. It's probable it is bash, but it might not be. Good call on the double quotes though. – Tim Angus Apr 26 '19 at 15:02
  • The parentheses could probably be changed to braces to avoid a subshell. You will also need to add a semicolon before the closing brace (this is a weird idiosyncracy in the POSIX shell grammar). – tripleee Apr 26 '19 at 15:43
  • 1
    Small improvement for when the mounted directory includes spaces, and the current `awk` would only give us part of the path, changing it to `awk 'END {$1=$2=""; print $0}'` to ignore the first two matches, and take the rest: `VOLUME=$(hdiutil attach -nobrowse '[DMG FILE]' | awk 'END {$1=$2=""; print $0}'; exit ${PIPESTATUS[0]}) && (rsync -a "$VOLUME"/*.app /Applications/; SYNCED=$? (hdiutil detach -force -quiet "$VOLUME" || exit $?) && exit "$SYNCED")` – Sergi Oct 12 '21 at 09:17