Kompaktes Starten von Mac OS-Apps über die Befehlszeile

22

Ich arbeite ziemlich viel in der Kommandozeile und finde mich dabei, viele Aliase der Form zu definieren:

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

Gibt es eine Möglichkeit, Magie so hinzuzufügen, dass die Abfrage der ausführbaren Datei "foo" automatisch die ausführbare Datei /Applications/Foo.app/Contents/MacOS/Foo verwendet? Dies ist unter OS X 10.6.

(Mir ist bewusst, dass dies möglich ist open foo.pdf, aber Skim ist nicht der Standard-PDF-Reader. Ich hätte gerne eine allgemeine Lösung. In einigen Fällen ist es nicht angebracht, die betreffende Anwendung als Standard-Handler für die Datei festzulegen.)

Reid
quelle
22
Sie können open -a Skim foo.pdf.
1
@mankoff: schön, ich habe vergessen, dass du mit -a offen sagen kannst, dass du eine App aus dem Applications-Verzeichnis willst. Sie sollten dies zu einer Antwort ändern :)
Robert S Ciaccio
@Reid: Könnten Sie Ihre Frage so bearbeiten, dass sie eine Beispielbefehlszeile enthält, die Sie eingeben möchten?
Robert S Ciaccio
@mankoff was bedeutet, dass er Aliase benötigt, um sicher zu sein oder die Tab-Vervollständigung zu verlängern
Robert S Ciaccio
@Reid irgendwelche Probleme mit der Lösung?

Antworten:

10

Endlich habe ich es verstanden: Fügen Sie dies zu Ihren hinzu .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()

Nun die folgende Arbeit:

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

Edit von Reid:

Ich habe das oben genannte als Python-Skript implementiert, das Wrapper-Skripte anstelle von Aliasen erstellt. Sie müssen ~/bin/mankoffmagicIhren Weg einschlagen. Wenn Sie möchten, dass die Wrapper automatisch aktualisiert werden, führen Sie sie regelmäßig von cron oder einem ähnlichen Computer aus.

#!/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
quelle
Das "grep -v iWork" ist da, nur weil das Apostroph in "iWork '09" die Dinge durcheinander bringt. Beachten Sie, dass Sie, wenn Sie Apps an einem anderen Ort speichern (~ / local / Applications), diese nur zum lsBefehl hinzufügen. Dies funktioniert auch dort.
Schön. Ich mag, dass es dynamisch ist, damit Sie keine Wartungsprobleme wie das Aufräumen alter Aliase haben.
Robert S Ciaccio
Vielen Dank @mankoff! Riecht gut - wenn ich es getestet habe und es funktioniert, werde ich die Antwort als akzeptiert markieren. Sehr geschätzt.
Reid
OK, das funktioniert. Bei Apps mit einem Apostroph (und vermutlich anderen lustigen Zeichen im Namen) kommt es zu Unterbrechungen. Ich habe über Ihre Lösung nachgedacht und etwas Ähnliches in Python gemacht, das ich in einer anderen Antwort veröffentlichen werde. Wenn dies jedoch nicht die richtige Etikette ist, kann ich sie auch gerne zu Ihrer Antwort hinzufügen (oder was auch immer).
Reid
8

Sie brauchen nichts Besonderes, Sie haben bereits die Antwort. Versuchen:

open /Applications/Foo.app bar.pdf

auf bearbeiten

Angesichts der folgenden Kommentare denke ich, dass meine Antwort immer noch relativ ähnlich wäre ... Ich würde eine Funktion erstellen, die open überschreibt, pushd to ausführt, callt /Applications, /usr/bin/open $appName.app $argspopd ausführt und zurückkehrt.

Ich mag Shell-Skripte, aber so etwas wie das Folgende, das spezielle Fälle abdeckt und Sie so ziemlich die gleiche Syntax wie Apple verwendet, die für Open vorgesehen ist. Ich möchte meine Umwelt so sauber wie möglich halten.

Ich bin mir sicher, dass die Syntax aus dem Weltraum stammt:

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
}

bei der zweiten Bearbeitung

Wenn Sie sich @mankoffs Kommentar zur ursprünglichen Frage ansehen, wären die meisten Dinge in der obigen Funktion wahrscheinlich Zeitverschwendung, da Sie sie nur verwenden könnten open -a $appName. Also hat mankoff wahrscheinlich die einfachste Lösung und sollte seinen Kommentar in eine Antwort ändern;)

Robert S. Ciaccio
quelle
Ich denke, @Reid möchte einen kürzeren Weg, ohne alle Aliase manuell zu erstellen.
Er muss jedoch keinen Alias ​​für die ausführbare Unix-Datei erstellen, wenn er "open" für das APP-Paket verwendet. Ich hatte den Eindruck, dass er Aliase erstellt, weil er dachte, die einzige Möglichkeit, eine App direkt über die Befehlszeile zu starten, bestehe darin, in das MacOS-Verzeichnis zu wechseln und die Datei auszuführen, die die 'echte' ausführbare Datei in einer App ist. In diesem Fall würde ein Alias ​​mindestens die Eingabe der zusätzlichen drei Ebenen der Verzeichnisstruktur sparen, die Sie zum Starten der App benötigen würden. Vielleicht verstehe ich seine Frage falsch.
Robert S Ciaccio
@calevara, danke - @mankoff ist richtig; es geht um die Länge.
Reid
3
Dies ist ein Missbrauch der Bearbeitungsfunktionen. Wenn Sie später eine zweite Antwort vorschlagen möchten, tun Sie genau das. Bearbeiten Sie Ihre ursprüngliche Antwort nicht, indem Sie die Stimmen, die sie erhalten hat, auf Ihre neue Idee übertragen.
Jeff Swensen
1
Ich stimme nicht zu ... die Antwort ist immer noch ziemlich ähnlich, da er die gleiche Syntax verwenden würde. Nicht nur das, aber wenn die neue Antwort den Wählern nicht gefällt, können sie ihre Stimme ändern, da die Antwort bearbeitet wurde. Genau aus diesem Grund können Sie Ihre Stimme nach der Bearbeitung ändern. Es ist auch der Grund, warum ich "on edit" fett geschrieben habe.
Robert S Ciaccio
4

Es gibt zwei Lösungen, die ich mir vorstellen kann:

Der einfachere Weg - Erstellen Sie mithilfe der Info.plistDatei in jedem .app- ContentsVerzeichnis einen Index des Werts für die Schlüssel CFBundleExecutable. Fügen Sie dann einen kurzen Alias ​​hinzu, der ein Skript (Perl, Python usw.) mit dem Namen einer ausführbaren Datei und den Argumenten aufruft, die Sie übergeben möchten.

Ihr Index besteht aus zwei ausführbaren Namen und Pfaden zu diesen ausführbaren Dateien. Sie müssten ein wiederkehrendes geplantes Skript schreiben, um dies auf dem neuesten Stand zu halten.

Sie könnten am Ende anrufen:

f foo file.txt

Dabei ist f ein Alias ​​für ein Skript, das Ihren Index auf eine ausführbare Datei mit dem Namen überprüft foo.

Alles in allem ist nicht viel Arbeit erforderlich, um die gewünschte Funktionalität zu erhalten.

Der schwierigere Weg - Erweitern Sie Ihre Shell, um die command_not_foundFehlerbehandlung zu ergänzen . Im Wesentlichen würden Sie eine Ruby- method_missingFunktionalität in der von Ihnen verwendeten Shell implementieren . Wenn command_not_foundausgelöst wurde, prüfte Ihre Methode die ausführbaren Namen Ihrer installierten Anwendungen.

Jeff Swensen
quelle
2

Applescript zur Rettung:

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

Ersetzen Sie den Namen der Anwendung durch die Anwendung, die Sie starten möchten, und fertig. Sie können es natürlich bei Bedarf zu einer Shell-Funktion machen:

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

und benutze es so:

f iTunes

Ich hasse AppleScript, aber es ist manchmal nützlich, und ich glaube, die einzige Möglichkeit, eine Anwendung einfach über den Namen in der Befehlszeile zu adressieren. Alles andere erfordert einen vollständigen Pfad.

Eric
quelle
Unterstützt keine Tab-Vervollständigung. Benötigt das genaue Merken von App-Namen. Sehen Sie nicht, warum AppleScript erforderlich ist (insbesondere, wenn Sie es hassen), wenn Shell-Scripting alles kann.
1
Ich habe nie bemerkt, dass open -a ohne Dateinamen als Argument verwendet werden kann. Und ich benutze Open seit NextStep manchmal in den frühen 90ern! Bitte reichen Sie dies als Antwort ein, damit ich darüber abstimmen kann. Also ja, die richtige Antwort ist 'open -a <Anwendungsname>'
eric