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.
Asked
Active
Viewed 4,557 times
2

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 Answers
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 volumeVOLUME=$(...; exit ${PIPESTATUS[0]})
SetVOLUME
to the output of the above, and set the exit code to that ofhdiutil
&&
If the disk image was mounted successfully...rsync -a "$VOLUME"/*.app /Applications/
...usersync
to copy the .app files to the/Applications
directory, while preserving permissions/symlinks/ownership etc.; SYNCED=$?
Store result ofrsync
operation(hdiutil detach -force -quiet "$VOLUME"
force unmount the disk image|| exit $?) && "$SYNCED"
Exit withhdiutil
exit code, orrsync
exit code ifhdiutil
succeeded

Tim Angus
- 983
- 11
- 26
-
1Putting 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
-
-
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
-
1Small 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