Last active
December 24, 2023 04:16
-
-
Save superkeyor/1d431a848426141707ebde736bbfc004 to your computer and use it in GitHub Desktop.
Bash script to install app on macOS from command line (can handle various situations)
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
function installapp { | |
local path=$1 | |
local quiet=$2 | |
if [ -z "$path" ]; then | |
echo "Usage:" | |
echo " installapp <path/to/directory> - Search a directory for dmg, pkg, zip files (in that order) to install." | |
echo " installapp <path/to/file> - Install a specific dmg, pkg, or zip file." | |
echo " installapp <URL> - Download and install a (dmg, pkg, zip) file from a URL." | |
echo " accepts -q or --quiet to auto confirm" | |
echo "" | |
echo "Examples:" | |
echo " installapp Acrobat/Mac" | |
echo " installapp Python/VSCode*.zip -q" | |
echo " installapp http://example.com/installer.dmg --quiet" | |
echo "" | |
echo "If an installer file is found, you will be asked for confirmation before installation." | |
return 1 | |
fi | |
# local for the outer function, but "global" to nested inner functions below | |
# otherwise, they will "leak" to rest script outside this function | |
# better, unset them at the end of script | |
local downloadTempd=$(mktemp -d) | |
local zipTempd=$(mktemp -d) | |
local file="" | |
prompt_for_confirmation() { | |
local message=$1 | |
if [[ "$quiet" == "-q" || "$quiet" == "--quiet" ]]; then | |
echo "$message ([y]/n):" # Optionally display the message | |
return 0 # Automatically confirm | |
fi | |
read -p "$message ([y]/n): " response | |
response=${response:-y} | |
if [[ $response == [Nn]* ]]; then | |
echo "User cancelled." | |
return 1 | |
fi | |
return 0 | |
} | |
handle_installation() { | |
local appfile=$1 | |
local volume=$2 | |
if [[ "$appfile" == *.pkg ]]; then | |
# sudo installer -pkg "$pkgfile" -target / | |
open -W "$appfile" # wait for pkg GUI to quit | |
else | |
local appfile_lower=$(echo "$appfile" | tr '[:upper:]' '[:lower:]') | |
if [[ "$appfile_lower" == *"install"* ]]; then | |
if prompt_for_confirmation "Plausible installer .app found ('$appfile'). Open it (y) or copy to /Applications (n)?"; then | |
echo "Opening installer app..." | |
open -W "$appfile" | |
else | |
echo "Copying $appfile to /Applications..." | |
sudo cp -rf "$appfile" /Applications | |
fi | |
else | |
echo "Copying $appfile to /Applications..." | |
sudo cp -rf "$appfile" /Applications | |
fi | |
fi | |
if [ -n "$volume" ]; then | |
sudo hdiutil detach "$volume" | |
fi | |
} | |
find_pkg_or_app() { | |
local search_path=$1 | |
local depth=$2 | |
local pkg_file=$(find "$search_path" -maxdepth "$depth" -type f -name "*.pkg" -print -quit) | |
if [ -n "$pkg_file" ]; then | |
echo "$pkg_file" | |
return | |
fi | |
local app_dir=$(find "$search_path" -maxdepth "$depth" -type d -name "*.app" -print -quit) | |
if [ -n "$app_dir" ]; then | |
echo "$app_dir" | |
fi | |
} | |
download_file() { | |
local url=$1 | |
local dest=$downloadTempd/$(basename "$url") | |
curl -L -o "$dest" "$url" | |
if [ $? -eq 0 ]; then | |
echo "$dest" | |
fi | |
} | |
process_path() { | |
if [[ $path == http* ]]; then | |
if prompt_for_confirmation "Found URL: $path. Do you want to download and install the file from this URL?"; then | |
file=$(download_file "$path") | |
if [ -n "$file" ]; then | |
return 0 | |
else | |
echo "Error downloading file." | |
return 1 | |
fi | |
fi | |
else | |
# Remove trailing slash if it exists for local paths | |
local search_path="${path%/}" | |
if [ -d "$search_path" ]; then | |
for extension in dmg pkg zip; do | |
for f in "$search_path"/*.$extension; do | |
if [ -f "$f" ] && prompt_for_confirmation "Found installer: $f. Do you want to install this?"; then | |
file=$f | |
return 0 | |
fi | |
done | |
done | |
elif [ -f "$search_path" ] && prompt_for_confirmation "Found file: $search_path. Do you want to install this file?"; then | |
file=$search_path | |
return 0 | |
fi | |
return 1 | |
fi | |
} | |
process_path || return 1 | |
case $file in | |
*.dmg) | |
local listing=$(sudo hdiutil attach "$file" | grep Volumes) | |
local volume=$(echo "$listing" | cut -f 3) | |
# Simulate a breadth-first search | |
for depth in 1 2; do | |
local appfile=$(find_pkg_or_app "$volume" $depth) | |
if [ -n "$appfile" ]; then | |
handle_installation "$appfile" "$volume" | |
break | |
fi | |
done | |
;; | |
*.pkg) | |
open -W "$file" | |
;; | |
*.zip) | |
unzip "$file" -d "$zipTempd" | |
for depth in 1 2; do | |
local appfile=$(find_pkg_or_app "$zipTempd" $depth) | |
if [ -n "$appfile" ]; then | |
handle_installation "$appfile" "" | |
break | |
fi | |
done | |
;; | |
esac | |
# echo "download: $downloadTempd" | |
# echo "zip: $zipTempd" | |
[ -n "$downloadTempd" ] && rm -rf "$downloadTempd" | |
[ -n "$zipTempd" ] && rm -rf "$zipTempd" | |
unset path quiet downloadTempd zipTempd file listing volume depth appfile | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment