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

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

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

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

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

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

Ответы (4)

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

#!/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()
«grep -v iWork» здесь только потому, что апостроф в «iWork '09» все портит. Обратите внимание: если вы храните приложения в другом месте (~/local/Applications), просто добавьте это в lsкоманду, и это тоже будет работать там.
хороший. мне нравится, что он динамический, поэтому у вас не возникает проблем с обслуживанием, таких как очистка старых псевдонимов.
Спасибо @mankoff! Прекрасно пахнет - как только я протестирую его и он сработает, я отмечу ответ как принятый. Очень признателен.
Хорошо, это работает. Он ломается в приложениях с апострофом (и, предположительно, другими забавными символами в названии). Я воспользовался вашим решением и сделал что-то подобное в Python, который я опубликую в другом ответе, хотя, если это не правильный этикет, я с удовольствием добавлю его в ваш ответ (или что-то еще).

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

open /Applications/Foo.app bar.pdf

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

В свете комментариев ниже, я думаю, что мой ответ все равно будет относительно похожим... Я бы сделал функцию, которая переопределяет open, выполняет pushd to /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. Так что у манкоффа, вероятно, самое простое решение, и он должен изменить свой комментарий на ответ;)

Я думаю, что @Reid хочет более короткий путь без ручного создания всех псевдонимов.
Но ему не нужно создавать псевдоним для исполняемого файла unix, если он использует «open» в пакете .app. У меня сложилось впечатление, что он создавал псевдонимы, потому что считал, что единственный способ запустить приложение непосредственно из командной строки — пройти весь путь до каталога MacOS и выполнить файл, который является «настоящим» исполняемым файлом внутри приложения. Таким образом, в этом случае псевдоним, по крайней мере, избавит вас от ввода дополнительных трех уровней структуры каталогов, которые вам понадобятся для запуска приложения. Может быть, я неправильно понимаю его вопрос.
@calevara, спасибо - @mankoff прав; речь идет о длине.
Хорошо, если я правильно понимаю, вы хотите просто ввести имя приложения, как если бы это была утилита командной строки на вашем пути? так $ foo bar.pdf?
Это немного неправильное использование функции редактирования. Если позже вы захотите предложить второй ответ, сделайте именно это. Не редактируйте свой первоначальный ответ, добавляя полученные голоса к своей новой идее.
Я не согласен ... ответ все еще очень похож, потому что он будет использовать тот же синтаксис. Не только это, но и если голосующим не нравится новый ответ, они могут изменить свой голос, потому что ответ был отредактирован. Именно поэтому вы можете изменить свой голос после редактирования. Именно поэтому я выделил жирным шрифтом «в редакции».
@mankoff: я согласен, твой ответ - лучший ответ и настоящее решение. Однако я не могу контролировать голосование людей, возможно, некоторые просто не понимают, что здесь делается, или они голосуют в знак протеста против тупых комментариев определенного комментатора. В любом случае, на самом деле речь идет о помощи человеку, который задал вопрос, и меня не очень волнуют голоса, пока спрашивающий получает то, что ему нужно. Надеюсь, Рид найдет ваш ответ полезным и выберет ваш ответ как принятый. если вас это утешит, один из ваших голосов от меня :)
и в любом случае, в целом, я думаю, что тот факт, что на этот вопрос есть 4 ответа с ~ 20 голосами, объединенными между собой и ответами, является хорошим признаком для этого сообщества. слишком много вопросов имеют только 1 или 2 ответа и 4 или 5 голосов.

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

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

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

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

f foo file.txt

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

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

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

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 (особенно если вы его ненавидите), если сценарии оболочки могут делать все это.
Я никогда не понимал, что open -a можно использовать без имени файла в качестве аргумента. И я использую open со времен NextStep иногда в начале 90-х! Пожалуйста, отправьте это как ответ, чтобы я мог проголосовать за него. Так что да, правильный ответ — использовать «open -a <имя приложения>».