Search code examples
macosplistosx-elcapitanlaunchdlaunchctl

Is it possible to evaluate an expression in launchd's ProgramArguments array?


I have a ~/Library/LaunchAgents/setenv.JAVA_HOME.plist file which contains a /bin/launchctl call as follows:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>setenv.JAVA_HOME</string>
    <key>ProgramArguments</key>
    <array>
        <string>/bin/launchctl</string>
        <string>setenv</string>
        <string>JAVA_HOME</string>
        <string>$(/usr/libexec/java_home -v1.8)</string>
    </array>
    <key>RunAtLoad</key>
    <true/>
    <key>ServiceIPC</key>
    <false/>
</dict>
</plist>

Problem is, the $(/usr/libexec/java_home -v1.8) expression is not evaluated and instead the JAVA_HOME environment variable is assigned the literal value $(/usr/libexec/java_home -v1.8).

Question is: is it possible to compose my plist file so that the expression is evaluated and not treated as a literal value? And, if so, how?


Solution

  • As you've discovered, <string> entries are passed as string literals, but $(/usr/libexec/java_home -v1.8) is a bash shell expression. According to the launchd.plist documentation, ProgramArguments only takes an array of strings, so there does not appear to be any way of marking an argument as an expression.

    However, I think the simple solution is to run /bin/bash with the -c argument, followed by the command line you want to execute.

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
    <plist version="1.0">
    <dict>
        <key>Label</key>
        <string>test.so</string>
        <key>ProgramArguments</key>
        <array>
            <string>/bin/bash</string>
            <string>-c</string>
            <string>/bin/launchctl setenv JAVA_HOME $(/usr/libexec/java_home -v1.8)</string>
        </array>
        <key>RunAtLoad</key>
        <true/>
        <key>ServiceIPC</key>
        <false/>
    </dict>
    </plist>