Zwięzłe uruchamianie aplikacji Mac OS z wiersza poleceń

22

Wykonuję sporo pracy w wierszu poleceń i definiuję wiele aliasów formy:

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

Czy istnieje sposób na dodanie magii, tak aby prośba o plik wykonywalny „foo” automatycznie używa pliku wykonywalnego /Applications/Foo.app/Contents/MacOS/Foo? To jest na OS X 10.6.

(Zdaję sobie sprawę, że mógłbym to zrobić open foo.pdf, ale Skim nie jest domyślnym czytnikiem plików PDF i chciałbym ogólne rozwiązanie - w niektórych przypadkach nie jest odpowiednie ustawienie danej aplikacji jako domyślnej procedury obsługi pliku).

Reid
źródło
22
Można open -a Skim foo.pdf.
1
@mankoff: fajnie, zapomniałem, że możesz użyć opcji -a, aby powiedzieć, że chcesz otworzyć aplikację z katalogu Aplikacje. powinieneś zmienić to na odpowiedź :)
Robert S Ciaccio
@Reid: czy możesz edytować swoje pytanie, aby zawierało przykładowy wiersz poleceń, który chcesz wpisać?
Robert S Ciaccio
@mankoff, co oznacza, że ​​potrzebuje pseudonimów dla pewności lub rozszerzenia tabulacji
Robert S Ciaccio
@ Czy masz jakieś problemy z rozwiązaniem?

Odpowiedzi:

10

Wreszcie mam: dodaj to do swojego .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()

Teraz następująca praca:

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

Edytuj przez Reid:

Zaimplementowałem powyższe jako skrypt w języku Python, który tworzy skrypty zamiast aliasów. Musisz postawić ~/bin/mankoffmagicna swojej drodze. Jeśli chcesz, aby opakowania były aktualizowane automatycznie, uruchamiaj je regularnie z crona lub z innej strony.

#!/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
źródło
Jest tam „grep -v iWork” tylko dlatego, że apostrof w „iWork '09” psuje wszystko. Pamiętaj, że jeśli przechowujesz aplikacje w innym miejscu (~ / local / Applications), po prostu dodaj to do lspolecenia, a to również tam zadziała.
niezłe. podoba mi się to, że jest dynamiczne, więc nie musisz mieć problemów z konserwacją, takich jak czyszczenie starych aliasów.
Robert S Ciaccio,
Dzięki @mankoff! Pachnie świetnie - kiedy go przetestuję i zadziała, zaznaczę odpowiedź jako zaakceptowaną. Bardzo mile widziane.
Reid,
OK, to działa. To psuje się w aplikacjach z apostrofem (i prawdopodobnie innymi śmiesznymi znakami w nazwie). Zastanawiałem się nad twoim rozwiązaniem i zrobiłem coś podobnego w Pythonie, który opublikuję w innej odpowiedzi - chociaż jeśli to nie jest właściwa etykieta, chętnie dodam to do twojej odpowiedzi (lub cokolwiek innego).
Reid
8

Nie potrzebujesz niczego wymyślnego, masz już odpowiedź. Próbować:

open /Applications/Foo.app bar.pdf

po edycji

W świetle poniższych komentarzy, myślę, że moja odpowiedź byłaby względnie podobna ... Zrobiłbym funkcję, która zastępuje otwarte, robi push-to /Applications, wywołuje /usr/bin/open $appName.app $args, wyskakuje i wraca.

Mam obsesję na punkcie pisania skryptów powłoki, ale coś w stylu poniżej, które obejmuje specjalne przypadki i utrzymuje prawie taką samą składnię, jak Apple dla open. Lubię dbać o środowisko tak czyste, jak to możliwe.

Jestem pewien, że składnia pochodzi z kosmosu:

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
}

przy drugiej edycji

patrząc na komentarz @ mankoffa do pierwotnego pytania, większość rzeczy w powyższej funkcji byłaby prawdopodobnie stratą czasu, ponieważ można po prostu użyć open -a $appName. Więc mankoff ma prawdopodobnie najłatwiejsze rozwiązanie i powinien zmienić swój komentarz na odpowiedź;)

Robert S. Ciaccio
źródło
Myślę, że @Reid chce krótszego sposobu bez ręcznego tworzenia wszystkich aliasów.
Ale nie musi tworzyć aliasu dla pliku wykonywalnego unixa, jeśli używa „otwartego” pakietu .app. Odniosłem wrażenie, że tworzył aliasy, ponieważ myślał, że jedynym sposobem na uruchomienie aplikacji bezpośrednio z wiersza poleceń jest przejście do katalogu MacOS i wykonanie pliku, który jest „prawdziwym” plikiem wykonywalnym w aplikacji. W takim przypadku alias zaoszczędziłby przynajmniej wpisując dodatkowe trzy poziomy struktury katalogów, które byłyby potrzebne do uruchomienia aplikacji. Może nie rozumiem jego pytania.
Robert S Ciaccio,
@calevara, dzięki - @mankoff ma rację; chodzi o długość.
Reid
3
Jest to trochę niewłaściwe użycie funkcji edycji. Jeśli później chcesz zaproponować drugą odpowiedź, zrób to. Nie edytuj oryginalnej odpowiedzi, nakładając głosy, które otrzymała na Twój nowy pomysł.
Jeff Swensen
1
Nie zgadzam się ... odpowiedź jest nadal dość podobna, ponieważ używałby tej samej składni. Nie tylko to, ale jeśli wyborcom nie podoba się nowa odpowiedź, mogą zmienić swój głos, ponieważ odpowiedź została zredagowana. Właśnie dlatego możesz zmienić swój głos po edycji. Dlatego też pogrubiłem „on edit”.
Robert S Ciaccio
4

Są dwa rozwiązania, o których mogę myśleć:

Info.plistProstszy sposób - korzystając z pliku w każdym Contentskatalogu .app , utwórz indeks wartości dla kluczy CFBundleExecutable. Następnie dodaj krótki alias, który wywołuje skrypt (perl, python, cokolwiek) z nazwą pliku wykonywalnego i argumentami, które chcesz przekazać.

Indeks byłby parami plików wykonywalnych i ścieżek do tych plików wykonywalnych. Będziesz musiał napisać cykliczny skrypt, aby aktualizować ten skrypt.

W końcu możesz zadzwonić:

f foo file.txt

gdzie f jest aliasem skryptu, który sprawdza w indeksie plik wykonywalny o nazwie foo.

Podsumowując, nie ma dużo pracy, aby uzyskać pożądaną funkcjonalność.

Trudniejszy sposób - Rozszerz swoją powłokę, aby uzupełnić obsługę jej command_not_foundbłędów. Zasadniczo zaimplementowałbyś method_missingfunkcjonalność w stylu Ruby w dowolnej używanej powłoce. Kiedy command_not_foundzostał rzucony, twoja metoda sprawdzi nazwy wykonywalnych zainstalowanych aplikacji.

Jeff Swensen
źródło
2

Jabłko na ratunek:

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

Zamień nazwę aplikacji na aplikację, którą chcesz uruchomić i gotowe. W razie potrzeby możesz oczywiście ustawić funkcję powłoki:

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

i użyj go w ten sposób:

f iTunes

Nienawidzę AppleScript, ale czasami jest przydatny i uważam, że jedynym sposobem na zaadresowanie aplikacji jest po prostu nazwa w wierszu poleceń. Cała reszta będzie wymagała pełnej ścieżki.

eric
źródło
Nie obsługuje uzupełniania tabulatorów. Wymaga dokładnego zapamiętania nazw aplikacji. Nie rozumiem, dlaczego AppleScript jest wymagany (zwłaszcza jeśli go nie znosisz), jeśli skrypty powłoki mogą to wszystko zrobić.
1
Nigdy nie zdawałem sobie sprawy, że open -a może być użyte bez nazwy pliku jako argumentu. I używam open od czasu NextStep czasami na początku lat 90-tych! Prześlij to jako odpowiedź, aby móc głosować nad tym. Tak, poprawną odpowiedzią jest użycie polecenia „open -a <nazwa aplikacji>”
eric