Краткий запуск приложений Mac OS из командной строки

22

Я выполняю значительную часть работы в командной строке и определяю множество псевдонимов формы:

alias skim='/Applications/Skim.app/Contents/MacOS/Skim'

Есть ли способ добавить магию так, чтобы при запросе исполняемого файла "foo" автоматически использовался исполняемый файл /Applications/Foo.app/Contents/MacOS/Foo? Это на OS X 10.6.

(Я знаю, что мог бы это сделать open foo.pdf, но Skim не является программой чтения PDF по умолчанию, и я хотел бы получить общее решение - в некоторых случаях нецелесообразно устанавливать соответствующее приложение в качестве обработчика по умолчанию для файла.)

Reid
источник
22
Вы можете open -a Skim foo.pdf.
1
@mankoff: хорошо, я забыл, что вы можете использовать -a, чтобы сообщить open, что вы хотите приложение из каталога Applications. Вы должны изменить это на ответ :)
Robert S Ciaccio
@Reid: не могли бы вы отредактировать свой вопрос, включив в него пример командной строки, которую вы хотите набрать?
Роберт С. Чаччо
@mankoff, что означает, что ему наверняка нужны псевдонимы или расширение табуляции
Robert S Ciaccio
@ Решить какие-либо проблемы с решением?

Ответы:

10

Наконец-то я понял: добавь это в свой .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()

Сейчас ведутся следующие работы:

Skim # open Skim.app
Skim foo.pdf
FireFox http://google.com
FireFox google.com # ERROR. Looks for local file.

Редактировать Рейд:

Я реализовал вышеупомянутое как скрипт Python, который создает сценарии-оболочки вместо псевдонимов. Вам нужно будет поставить ~/bin/mankoffmagicсвой путь. Если вы хотите, чтобы обертки обновлялись автоматически, регулярно запускайте их с помощью cron или 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" "$@"
#open -a "%s" "$@"
""" % (path, appdir, appname, appname))
   fp.close()
   os.chmod(wrapper, 0700)


if (__name__ == "__main__"):
   main()
jherran
источник
"Grep -v iWork" присутствует там только потому, что апостроф в "iWork '09" все портит. Обратите внимание, что если вы храните приложения в другом месте (~ / local / Applications), просто добавьте это в lsкоманду, и это тоже будет работать.
хороший. Мне нравится, что он динамический, поэтому вы не сталкиваетесь с какими-либо проблемами обслуживания, такими как очистка старых псевдонимов.
Роберт С. Чаччо
Спасибо @mankoff! Прекрасно пахнет - как только я проверил это, и оно работает, я отмечу ответ принятым. Очень признателен.
Рейд
ОК, это работает. Это ломает приложения с апострофом (и, возможно, другими забавными символами в названии). Я набросал ваше решение и сделал что-то похожее на Python, которое я опубликую в другом ответе - хотя, если это неправильный этикет, я с радостью добавлю его к вашему ответу (или как угодно).
Рейд
8

Вам не нужно ничего особенного, у вас уже есть ответ. Пытаться:

open /Applications/Foo.app bar.pdf

на редактировании

В свете комментариев ниже, я думаю, что мой ответ все еще будет относительно похожим ... Я бы сделал функцию, которая переопределяет open, выполняет pushd /Applications, вызывает /usr/bin/open $appName.app $args, выполняет popd и возвращает.

Я отстой от сценариев оболочки, но что-то вроде ниже, который охватывает особые случаи и позволяет вам использовать почти тот же синтаксис, что и Apple, предусмотренный для open. Мне нравится, чтобы моя среда была максимально чистой.

Я уверен, что синтаксис из космоса:

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
}

на втором редактировании

Если посмотреть на комментарий @ mankoff к исходному вопросу, большая часть описанной выше функции, вероятно, будет пустой тратой времени, поскольку вы можете просто ее использовать open -a $appName. Так что у mankoff, наверное, самое простое решение, и он должен поменять свой комментарий на ответ;)

Роберт С Чаччо
источник
Я думаю, что @Reid хочет более короткий путь без ручного создания всех псевдонимов.
Но ему не нужно создавать псевдоним исполняемого файла unix, если он использует «open» в пакете .app. У меня сложилось впечатление, что он создает псевдонимы, потому что он думал, что единственный способ запустить приложение непосредственно из командной строки - это пройти весь путь в каталог MacOS и выполнить файл, который является «настоящим» исполняемым файлом внутри приложения. Таким образом, в этом случае псевдоним как минимум сохранит набор трех дополнительных уровней структуры каталогов, которые понадобятся вам для запуска приложения. Может быть, я неправильно понимаю его вопрос.
Роберт С Чаччо
@calevara, спасибо - @mankoff прав; это о длине.
Рейд
3
Это немного злоупотребляет функциональностью редактирования. Если позже вы захотите предложить второй ответ, сделайте именно это. Не редактируйте свой исходный ответ, совмещая полученные голоса с вашей новой идеей.
Джефф Свенсен
1
Я не согласен ... ответ все еще довольно похож, потому что он использовал бы тот же синтаксис. Мало того, но если избирателям не понравится новый ответ, они могут изменить свой голос, потому что ответ был отредактирован. Именно поэтому вы можете изменить свой голос после внесения изменений. Это также, почему я поставил «на редактирование» жирным шрифтом.
Роберт С Чаччо
4

Есть два решения, которые я могу придумать:

Более простой способ - используя Info.plistфайл в каждом каталоге .app Contents, создайте индекс значения для ключей CFBundleExecutable. Затем добавьте короткий псевдоним, который вызывает скрипт (perl, python и т. Д.) С именем исполняемого файла и аргументами, которые вы хотите передать.

Ваш индекс будет парами исполняемых имен и путей к этим исполняемым файлам. Вы должны написать повторяющийся запланированный сценарий, чтобы сохранить это обновление.

Вы в конечном итоге сможете позвонить:

f foo file.txt

где f - псевдоним скрипта, который проверяет ваш индекс на наличие исполняемого файла с именем foo.

В общем, не так много работы, чтобы получить функциональность, которую вы хотели бы.

Более сложный способ - расширить вашу оболочку, чтобы дополнить обработку ее command_not_foundошибки. По сути, вы бы реализовали функциональность стиля Ruby method_missingв любой оболочке, которую вы используете. Когда command_not_foundбыл брошен, ваш метод будет проверять имена исполняемых файлов ваших установленных приложений.

Джефф Свенсен
источник
2

Applescript на помощь:

osascript -e 'tell application "iTunes"' -e "activate" -e 'end tell'

Замените название приложения на приложение, которое вы хотите запустить, и все готово. Вы можете, конечно, сделать это функцией оболочки при необходимости:

function f() {
    osascript <<EOF
tell application "$1"
  activate
end tell
EOF
}

и используйте это так:

f iTunes

Я ненавижу appleScript, но иногда это полезно, и я считаю, что единственный способ обратиться к приложению просто по имени в командной строке. Все остальное потребует полного пути.

Эрик
источник
Не поддерживает завершение табуляции. Требуется точное запоминание названий приложений. Не понимаю, почему требуется AppleScript (особенно, если вы его ненавидите), если сценарии оболочки могут делать все это.
1
Я никогда не понимал, что open -a можно использовать без имени файла в качестве аргумента. И я использую open с NextStep иногда в начале 90-х! Пожалуйста, отправьте это как ответ, чтобы я мог проголосовать за него. Так что да, правильный ответ - использовать 'open -a <имя приложения>'
eric