Starten Sie Mac OS-Apps präzise von der Befehlszeile aus

Ich arbeite ziemlich viel in der Befehlszeile und ertappe mich dabei, wie ich viele Aliase des Formulars definiere:

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

Gibt es eine Möglichkeit, Magie hinzuzufügen, sodass die Abfrage nach 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 ich das tun könnte open foo.pdf, aber Skim ist nicht der Standard-PDF-Reader, und 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.)

Sie können open -a Skim foo.pdf.
@mankoff: Schön, ich habe vergessen, dass Sie mit -a sagen können, dass Sie eine App aus dem Anwendungsverzeichnis haben möchten. Sie sollten dies in eine Antwort ändern :)
@Reid: Könnten Sie Ihre Frage so bearbeiten, dass sie eine Beispielbefehlszeile enthält, die Sie eingeben möchten?
@mankoff, was bedeutet, dass er auf jeden Fall Aliase oder eine Erweiterung der Tab-Vervollständigung benötigt
@Reid irgendwelche Probleme mit der Lösung?

Antworten (4)

Endlich habe ich es verstanden: Fügen Sie dies zu Ihrer 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 folgende Arbeit:

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

Bearbeiten von Reid:

Ich habe das Obige als Python-Skript implementiert, das Wrapper-Skripte anstelle von Aliasen erstellt. Sie müssen ~/bin/mankoffmagicIhren Pfad eingeben. Wenn Sie möchten, dass die Wrapper automatisch aktualisiert werden, führen Sie sie regelmäßig von cron oder so 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()
Das "grep -v iWork" ist da drin, nur weil der Apostroph in "iWork '09" die Dinge durcheinander bringt. Beachten Sie, dass wenn Sie Apps woanders speichern (~/local/Applications), fügen Sie das einfach zum lsBefehl hinzu, und dies wird auch dort funktionieren.
Schön. Ich mag es, dass es dynamisch ist, sodass Sie keine Wartungsprobleme haben, wie z. B. das Aufräumen alter Aliase.
Danke @mankoff! Riecht toll - sobald ich es getestet habe und es funktioniert, werde ich die Antwort als akzeptiert markieren. Sehr geschätzt.
Okay, das funktioniert. Es bricht bei Apps mit einem Apostroph (und vermutlich anderen lustigen Zeichen im Namen) ab. Ich habe Ihre Lösung überarbeitet und etwas Ähnliches in Python gemacht, das ich in einer anderen Antwort veröffentlichen werde. Wenn dies jedoch nicht die richtige Etikette ist, füge ich es stattdessen gerne Ihrer Antwort hinzu (oder was auch immer).

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

open /Applications/Foo.app bar.pdf

auf Bearbeiten

In Anbetracht der folgenden Kommentare denke ich, dass meine Antwort immer noch relativ ähnlich wäre ... Ich würde eine Funktion erstellen, die open überschreibt, ein pushd zu macht /Applications, aufruft /usr/bin/open $appName.app $args, ein popd macht und zurückkehrt.

Ich sauge an Shell-Scripting, aber etwas wie unten, das Sonderfälle abdeckt und Sie dazu bringt, so ziemlich die gleiche Syntax zu verwenden, die Apple für open bereitgestellt hat. Ich halte meine Umgebung gerne so sauber wie möglich.

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 zweiter Bearbeitung

Wenn Sie den Kommentar von @mankoff zur ursprünglichen Frage betrachten, wäre das meiste in der obigen Funktion wahrscheinlich Zeitverschwendung, da Sie einfach verwenden könnten open -a $appName. Mankoff hat also wahrscheinlich die einfachste Lösung und sollte seinen Kommentar in eine Antwort ändern;)

Ich denke, @Reid möchte einen kürzeren Weg, ohne alle Aliase manuell zu erstellen.
Aber er muss 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 erstellte, weil er dachte, dass der einzige Weg, eine App direkt von der Befehlszeile aus zu starten, darin bestand, den ganzen Weg in das MacOS-Verzeichnis zu gehen und die Datei auszuführen, die die „echte“ ausführbare Datei in einer App ist. In diesem Fall würde ein Alias ​​also zumindest das Eintippen der zusätzlichen drei Ebenen der Verzeichnisstruktur ersparen, die Sie benötigen würden, um die App zu starten. Vielleicht verstehe ich seine Frage falsch.
@calevara, danke - @mankoff hat recht; es geht um die länge.
Ok, wenn ich das jetzt richtig verstehe, möchten Sie einfach den App-Namen eingeben, als wäre es ein Befehlszeilenprogramm in Ihrem Pfad? also $ foo bar.pdf?
Dies ist ein bisschen ein Missbrauch der Bearbeitungsfunktion. Wenn Sie später eine zweite Antwort vorschlagen möchten, tun Sie genau das. Bearbeiten Sie Ihre ursprüngliche Antwort nicht und übertragen Sie die Stimmen, die sie erhalten hat, auf Ihre neue Idee.
Ich stimme nicht zu ... die Antwort ist immer noch ziemlich ähnlich, weil er dieselbe Syntax verwenden würde. Nicht nur das, aber wenn Up-Voter die neue Antwort nicht mögen, können sie ihre Stimme ändern, weil die Antwort bearbeitet wurde. Genau aus diesem Grund können Sie Ihre Bewertung nach Änderungen ändern. Das ist auch der Grund, warum ich "on edit" fett gesetzt habe.
@mankoff: Ich stimme zu, Ihre Antwort ist die bessere Antwort und die echte Lösung. Ich kann die Abstimmung der Leute jedoch nicht kontrollieren, vielleicht verstehen einige einfach nicht, was hier getan wird, oder sie stimmen aus Protest gegen die bescheuerten Kommentare eines bestimmten Kommentators ab. In jedem Fall geht es wirklich darum, der Person zu helfen, die die Frage gestellt hat, und ich kümmere mich nicht wirklich um Stimmen, solange der Fragesteller bekommt, was er braucht. Hoffentlich findet Reid Ihre hilfreiche Antwort und wählt Ihre Antwort als akzeptiert aus. falls es dich tröstet, eine deiner Stimmen ist von mir :)
und insgesamt denke ich, dass die Tatsache, dass diese Frage 4 Antworten mit ~ 20 Stimmen zusammen mit den Antworten hat, ein gesundes Zeichen für diese Community ist. zu viele Fragen haben nur 1 oder 2 Antworten und 4 oder 5 Stimmen insgesamt.

Da fallen mir zwei Lösungen ein:

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

Ihr Index besteht aus Paaren ausführbarer 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

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

Alles in allem nicht viel Arbeit, 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 method_missingFunktionalität im Ruby-Stil in der von Ihnen verwendeten Shell implementieren. Wenn command_not_foundausgelöst wurde, würde Ihre Methode die ausführbaren Namen Ihrer installierten Anwendungen überprüfen.

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 Sie sind 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 auf der Befehlszeile anzusprechen. Alles andere erfordert einen vollständigen Pfad.

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