How to get an application executable path from Terminal

applicationspathterminal

In macOS, applications can be launched from the command line with open -a < application name>.

I would like to know if there is a command that returns the executable within
the application bundle, when the application is not running, I'm hoping for something like this:

awesomeCommand <application name>
/Applications/***/MacOS/Content/application_executable

Best Answer

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:

  1. Searches /Applications for the application bundle.
  2. If it can not be found in /Applications, queries the Launch Services database, only considering application bundles on the startup disk.
  3. If an application bundle was found, inspects the bundle's Info.plist and looks for a key namedCFBundleExecutable that stores the executable (see below in the background information section for more on that). Otherwise, print an error message an exit.
  4. 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 | grep -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 | grep -v /Volumes | egrep --max-count 1 "/$app_name$" | sed 's:.* \(/.*\):\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/$(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 pbex for "print bundle executable") and make it executable as follows:

chmod a+x pbex

If you prefer a function, use this instead:

function pbex ()
{ 
    # 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 | grep -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 | grep -v /Volumes | egrep --max-count 1 "/$app_name$" | sed 's:.* \(/.*\):\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/$(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 | grep -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 | grep -v /Volumes | egrep --max-count 1 \"/\$app_name\$\" | sed \"s:.* \(/.*\):\1:\")\"; if [[ -r \"\$app_path_and_name/Contents/Info.plist\" ]]; then echo \"\$app_path_and_name/MacOS/\$(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:

$ pbex
pbex: returns name of bundle applications’s executable file
usage: pbex [application name]
$ pbex "Microsoft Edge"
Application 'Microsoft Edge' not found
$ pbex Finder
/System/Library/CoreServices/Finder.app/MacOS/Finder
$ pbex "Quick Look Simulator.app"
/System/Library/Frameworks/Quartz.framework/Versions/A/Frameworks/QuickLookUI.framework/Versions/A/Resources/Quick Look Simulator.app/MacOS/Quick Look Simulator
$ pbex "BalTax 2016"
/Applications/BalTax 2016/BalTax 2016.app/MacOS/JavaApplicationStub
$ pbex ColorSync\ Utility
/Applications/Utilities/ColorSync Utility.app/MacOS/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."