I would like to know if there is a command that returns the executable within the application bundle
There is no such command, but I've written a shell script that does exactly that for any application bundle in the file system of your startup disk.
It uses several command line tools included in macOS and leverages Apple's documentation on Launch Services, Application Bundles and Core Foundation Keys. The script does the following:
- It searches
/Applications
for the app bundle.
- If the app bundle can not be found there, the script queries the Launch Services database, only considering app bundles on the startup disk.
- If an app bundle is found, the script inspects the bundle's
Info.plist
and looks for a key namedCFBundleExecutable
. This key stores the executable file (see below in the background information section for more on that). Otherwise, print an error message an exit.
- Prints the path to the executable to standard output.
The script understands both an application name with or without its extension, checks arguments, displays a usage message and a short description if run without arguments and returns an exit status with an error message if necessay.
Since the script searches /Applications
and may do a time-consuming query to Launch Services, it isn't super fast (from ~ 1 sec to several seconds), but it was 100% accurate in my tests.
This is the script, with explanations on what is done in every step:
#!/bin/bash
# https://apple.stackexchange.com/a/334635
# Variables
app_name="";
app_path_and_name="";
path_to_lsregister="/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/LaunchServices.framework/Versions/A/Support/";
# If run without arguments, issue a usage summary and exit
if [[ "$1" == "" ]]; then
echo "$(basename $0): returns name of bundle applications’s executable file";
echo "usage: $(basename $0) [application name]";
exit 0;
fi;
# If argument doesn't end with '.app', append it
if [[ "$1" =~ \.app$ ]]; then
app_name="$1"
else
app_name="$1.app";
fi;
# Look for the path of the application bundle
# Search /Applicatinos first
app_path_and_name="$(find /Applications -type d -name "$app_name" -maxdepth 5 | fgrep -m 1 "$app_name")";
# If not found, search the the LaunchServices database (this is the time-consuming task)
test "$app_path_and_name" || app_path_and_name="$($path_to_lsregister/lsregister -dump | fgrep -v '/Volumes|/System' | egrep --max-count 1 "path: */.*/$app_name " | sed 's:.* \(/.*app\) .*:\1:')"
#echo $app_path_and_name
# Check if Info.plist exists and is readable
if [[ -r "$app_path_and_name/Contents/Info.plist" ]]; then
# Extract the CFBundleExecutable key that contains the name of the executable and print it to standard output
echo "$app_path_and_name/MacOS/Contents/$(defaults read "$app_path_and_name/Contents/Info.plist" CFBundleExecutable)";
exit 0;
else
echo "Application '$1' not found";
exit 1
fi
You can save the file (for example as appath
) and make it executable as follows:
chmod a+x appath
If you prefer a function, use this instead:
function appath ()
{
# https://apple.stackexchange.com/a/334635
# Variables
local app_name="";
local app_path_and_name="";
local path_to_lsregister="/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/LaunchServices.framework/Versions/A/Support/";
# If run without arguments, issue a usage summary and exit
if [[ "$1" == "" ]]; then
echo "$FUNCNAME: returns name of bundle applications’s executable file";
echo "usage: $FUNCNAME [application name]";
return 0;
fi;
# If argument doesn't end with '.app', append it
if [[ "$1" =~ \.app$ ]]; then
app_name="$1"
else
app_name="$1.app";
fi;
# Look for the path of the application bundle
# Search /Applicatinos first
app_path_and_name="$(find /Applications -type d -name "$app_name" -maxdepth 5 | fgrep -m 1 "$app_name")";
# If not found, search the LaunchServices database (this is the time-consuming step)
test "$app_path_and_name" || app_path_and_name="$($path_to_lsregister/lsregister -dump | fgrep -v '/Volumes|/System' | egrep --max-count 1 "path: */.*/$app_name " | sed 's:.* \(/.*app\) .*:\1:')"
# Check if Info.plist exists and is readable
if [[ -r "$app_path_and_name/Contents/Info.plist" ]]; then
# Extract the CFBundleExecutable key that contains the name of the executable and print it to standard output
echo "$app_path_and_name/MacOS/Contents/$(defaults read "$app_path_and_name/Contents/Info.plist" CFBundleExecutable)";
return 0;
else
echo "Application '$1' not found";
return 1
fi
}
Just add it to .bashrc
, source it:
. .bashrc
to use the function.
If you need to execute it as a (rather long) "one-liner", use (replace <app name>
with the name of the application without the .app extension):
bash -c "app_name=\"<app name>.app\"; app_path_and_name=\"\"; if [[ \"\$app_name\" == \"\" ]]; then exit 2; fi; app_path_and_name=\"\$(find /Applications -type d -name \"\$app_name\" -maxdepth 5 | fgrep -m 1 \"\$app_name\")\"; test \"\$app_path_and_name\" || app_path_and_name=\"\$(/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/LaunchServices.framework/Versions/A/Support/lsregister -dump | fgrep -v \"/Volumes|/System\" | egrep --max-count 1 \"/\$app_name \" | sed \"s:.* \(/.*app\) .*:\1:\")\"; if [[ -r \"\$app_path_and_name/Contents/Info.plist\" ]]; then echo \"\$app_path_and_name/MacOS/Contents/\$(defaults read \"\$app_path_and_name/Contents/Info.plist\" CFBundleExecutable)\"; exit 0; else echo \"Application \"\$app_name\" not found\"; exit 1; fi"
(You write in a comment that the "command has to be launched from within a QProcess"). You could do the following:
process.start("bash", QStringList() << "-c" << "app_name=\"<app name>\.app"; ...");
See https://stackoverflow.com/a/20901985 for more information.)
Examples:
$ appath
appath: returns name of bundle applications’s executable file
usage: appath [application name]
$ appath "Microsoft Edge"
Application 'Microsoft Edge' not found
$ appath Finder
/System/Library/CoreServices/Finder.app/MacOS/Contents/Finder
$ appath "Simulator.app"
/Applications/Xcode.app/Contents/Developer/Applications/Simulator.app/MacOS/Contents/Simulator
$ appath "BalTax 2019"
/Applications/BalTax 2019/BalTax 2019.app/MacOS/Contents/JavaApplicationStub
$ appath ColorSync\ Utility
/Applications/Utilities/ColorSync Utility.app/MacOS/Contents/ColorSync Utility
BACKGROUND INFORMATION
The structure of a macOS bundle app bundle is well-known and documented by Apple.
According to The Structure of a macOS Application Bundle, the basic structure of a Mac app is:
MyApp.app/
Contents/
Info.plist
MacOS/
Resources/
where the MacOS
folder "contains the application’s standalone executable code. Typically, this directory contains only one binary file with your application’s main entry point and statically linked code. However, you may put other standalone executables (such as command-line tools) in this directory as well."
The Info.plist
file is required for the Finder to recognize an application bundle as such. This information property list filecontains XML property-list data that identifies the configuration of your bundle. The key that is of interest for us is CFBundleExecutable
, which stores "the name of the main executable file. This is the code that is executed when the user launches your application."