Concisely beginning Mac OS applications from the command line
I do a reasonable quantity of operate in the command line, and also I locate myself specifying a great deal of pen names of the kind:
alias skim='/Applications/Skim.app/Contents/MacOS/Skim'
Is there a means to add magic such that requesting for executable "foo" instantly makes use of executable/ Applications/Foo. app/Contents/MacOS/ Foo? This gets on OS X 10.6.
(I'm mindful that I can do open foo.pdf
, yet Skim is not the default PDF viewers, and also I would certainly such as a basic remedy - in many cases, it is not ideal to set the application concerned as the default trainer for the documents.)
There are 2 remedies that I can consider:
The less complicated means - Using the Info.plist
documents in each.app Contents
directory site, create an index of the value for the keys CFBundleExecutable. After that add a brief pen names that calls a script (perl, python, whatever) with the name of an executable and also the debates you would certainly such as to pass.
Your index would certainly be sets of executable names and also courses to those executables. You would certainly need to write a reoccuring set up script to maintain this upgraded.
You would certainly wind up having the ability to call:
f foo file.txt
where f is a pen name to a script that examines your index for an executable called foo
.
In conclusion, not a great deal of job to get the capability you would certainly such as.
The tougher means - Extend your covering to supplement the handling of its command_not_found
mistake. Basically you would certainly implement a Ruby design method_missing
capability within whatever covering you are making use of. When command_not_found
was tossed, your method would certainly examine the executable names of your mounted Applications.
Applescript to the rescue:
osascript -e 'tell application "iTunes"' -e "activate" -e 'end tell'
Replace the name of the application with the application you intend to start and also you are done. You can certainly make it a covering function if required:
function f() {
osascript <<EOF
tell application "$1"
activate
end tell
EOF
}
and also utilize it in this way:
f iTunes
I despise applescript, yet it serves occasionally, and also I think the only means to resolve an application merely by name on the command line. Every little thing else will certainly call for a complete course.
Finally I obtained it:.
Add this to your .bash_profile
function foomagick() {
rm -f ~/.foomagick.tmp
ls /Applications/ | grep "\.app" | grep -v iWork | while read APP; do
# clean it up
a=`echo $APP | sed s/\ //g`;
a=`echo $a | sed s/\'//g`;
echo alias ${a%.*}="'open -a \"${APP%.*}\"'" >> ~/.foomagick.tmp
done
source ~/.foomagick.tmp
rm ~/.foomagick.tmp
}
foomagick()
Now the adhering to job:
Skim # open Skim.app
Skim foo.pdf
FireFox http://google.com
FireFox google.com # ERROR. Looks for local file.
Edit by Reid:
I applied the above as a Python script that makes wrapper manuscripts as opposed to pen names. You will certainly require to place ~/bin/mankoffmagic
in your course. If you desire the wrappers to be upgraded instantly, run it consistently from cron or somesuch.
#!/usr/bin/python
#
# This script automagically updates a set of wrapper shell scripts in
# ~/bin/mankoffmagic which call Mac apps installed in /Applications.
#
# Inspired by mankoff's shell alias posted on apple.stackexchange.com; see:
# http://apple.stackexchange.com/questions/4240/concisely-starting-mac-os-apps-from-the-command-line/4257#4257
#
# Notes/Bugs:
#
# 1. Does not follow symlinks (aliases?)
#
# 2. Assumes that application names do not contain double-quotes.
#
# 3. Not very smart about finding the actual binary (it guesses). This is
# wrong sometimes, e.g. Firefox. Probably a deeper understanding of the app
# package structure would fix this.
import copy
import glob
import os
import os.path
import re
BINDIR = os.path.expandvars("$HOME/bin/mankoffmagic")
APP_RE = re.compile(r'(.*)\.app$')
STRIP_RE = re.compile(r'[\W_]+')
def main():
# We aggressively delete everything already in BINDIR, to save the trouble
# of analyzing what should stay
for f in glob.glob("%s/*" % BINDIR):
os.unlink(f)
# Walk /Applications and create a wrapper shell script for each .app dir
for (root, dirs, files) in os.walk("/Applications"):
dirs_real = copy.copy(dirs) # so we can manipulate dirs while looping
for d in dirs_real:
#print "checking %s" % os.path.join(root, d)
m = APP_RE.search(d)
if (m is not None):
#print "Found " + m.group()
dirs.remove(d) # no need to recurse into app
create_script(root, d, m.group(1))
def create_script(path, appdir, appname):
# remove non-alphanumerics and downcase it
wrapper = STRIP_RE.sub('', appname).lower()
wrapper = os.path.join(BINDIR, wrapper)
fp = open(wrapper, "w")
# Twiddle the comments in the script depending on whether you want to
# invoke the binary or use "open" -- the former lets you use any
# command-line args, while the latter is more Mac-like (app puts itself in
# the front, etc.)
fp.write("""
#!/bin/sh
exec "%s/%s/Contents/MacOS/%s" "[email protected]"
#open -a "%s" "[email protected]"
""" % (path, appdir, appname, appname))
fp.close()
os.chmod(wrapper, 0700)
if (__name__ == "__main__"):
main()
You do not require anything expensive, you currently have the solution. Attempt:
open /Applications/Foo.app bar.pdf
on edit
In light of the remarks listed below, I assume my solution would certainly still be reasonably comparable ... I would certainly make a function that bypasses open, does a pushd to /Applications
, calls /usr/bin/open $appName.app $args
, does a popd and also returns.
I draw at covering scripting yet something like listed below which covers grandfather clauses and also maintains you making use of virtually the very same syntax as apple attended to open. I such as to maintain my setting as tidy as feasible.
I'm certain the syntax is from celestial spaces:
function open($appName, $args)
{
#if we are calling open without a path like /Applications/TextEdit.app AND
# $appName ends in '.app'
if (!hasParent($appName) && isApp($appName))
{
pushd /Applications
/usr/bin/open ${appName}.app $args &
popd
return
}
#otherwise, use open as normal
/usr/bin/open $appName $args
return
}
on 2nd edit
looking @mankoff is talk about the initial inquiry, a lot of right stuff in the above function would possibly be a wild-goose chase given that you can simply make use of open -a $appName
. So mankoff possibly has the most convenient remedy and also needs to transform his comment to a solution ;)
Related questions