Démarrage concis des applications Mac OS à partir de la ligne de commande

22

Je fais pas mal de travail en ligne de commande, et je me retrouve à définir pas mal d'alias du formulaire:

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

Existe-t-il un moyen d'ajouter de la magie de telle sorte que demander l'exécutable "foo" utilise automatiquement l'exécutable /Applications/Foo.app/Contents/MacOS/Foo? C'est sur OS X 10.6.

(Je sais que je pourrais le faire open foo.pdf, mais Skim n'est pas le lecteur PDF par défaut, et j'aimerais une solution générale - dans certains cas, il n'est pas approprié de définir l'application en question comme gestionnaire par défaut du fichier.)

Reid
la source
22
Tu peux open -a Skim foo.pdf.
1
@mankoff: sympa, j'ai oublié que vous pouviez utiliser -a pour dire à open que vous voulez une application depuis le répertoire Applications. vous devriez changer cela en une réponse :)
Robert S Ciaccio
@Reid: pourriez-vous modifier votre question pour inclure un exemple de ligne de commande que vous cherchez à saisir?
Robert S Ciaccio
@mankoff ce qui signifie qu'il a besoin d'alias pour sûr ou d'une extension de la tabulation
Robert S Ciaccio
@Reid des problèmes avec la solution?

Réponses:

10

Enfin j'ai compris: ajoutez ceci à votre .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()

Maintenant, le travail suivant:

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

Édité par Reid:

J'ai implémenté ce qui précède comme un script Python qui crée des scripts wrapper au lieu d'alias. Vous devrez mettre ~/bin/mankoffmagicvotre chemin. Si vous souhaitez que les wrappers soient mis à jour automatiquement, exécutez-le régulièrement à partir de cron ou 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
la source
Le "grep -v iWork" est là juste parce que l'apostrophe dans "iWork '09" gâche les choses. Notez que si vous stockez des applications ailleurs (~ / local / Applications) ajoutez simplement cela à la lscommande, et cela fonctionnera là aussi.
joli. j'aime que c'est dynamique afin que vous ne vous retrouviez pas avec des problèmes de maintenance comme le nettoyage d'anciens alias.
Robert S Ciaccio
Merci @mankoff! Ça sent bon - une fois que je l'ai testé et qu'il fonctionne, je marquerai la réponse comme acceptée. Très appréciée.
Reid
OK, ça marche. Il se casse sur les applications avec une apostrophe (et probablement d'autres caractères drôles du nom). J'ai exploré votre solution et j'ai fait quelque chose de similaire en Python, que je publierai dans une autre réponse - bien que si ce n'est pas la bonne étiquette, je suis heureux de l'ajouter à votre réponse à la place (ou autre).
Reid
8

Vous n'avez besoin de rien d'extraordinaire, vous avez déjà la réponse. Essayer:

open /Applications/Foo.app bar.pdf

lors de l'édition

À la lumière des commentaires ci-dessous, je pense que ma réponse serait encore relativement similaire ... Je ferais une fonction qui remplace open, fait un pushd to /Applications, appelle /usr/bin/open $appName.app $args, fait un popd et retourne.

Je crains les scripts shell mais quelque chose comme ci-dessous qui couvre des cas spéciaux et vous permet d'utiliser à peu près la même syntaxe qu'Apple pour open. J'aime garder mon environnement aussi propre que possible.

Je suis sûr que la syntaxe vient de l'espace:

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
}

au deuxième montage

en regardant le commentaire de @ mankoff sur la question d'origine, la plupart des éléments de la fonction ci-dessus seraient probablement une perte de temps car vous pourriez simplement l'utiliser open -a $appName. Donc mankoff a probablement la solution la plus simple et devrait changer son commentaire en réponse;)

Robert S Ciaccio
la source
Je pense que @Reid veut un moyen plus court sans créer manuellement tous les alias.
Mais il n'a pas à créer d'alias pour l'exécutable unix s'il utilise 'open' sur le paquet .app. J'ai eu l'impression qu'il créait des alias parce qu'il pensait que la seule façon de démarrer une application directement à partir de la ligne de commande était d'aller jusqu'au répertoire MacOS et d'exécuter le fichier qui est le `` vrai '' exécutable à l'intérieur d'une application. Donc, dans ce cas, un alias sauverait au moins la saisie des trois niveaux supplémentaires de structure de répertoires dont vous auriez besoin pour démarrer l'application. Peut-être que je comprends mal sa question.
Robert S Ciaccio
@calevara, merci - @mankoff a raison; c'est de la longueur.
Reid
3
C'est un peu une mauvaise utilisation de la fonctionnalité d'édition. Si vous souhaitez par la suite proposer une deuxième réponse, faites-le. Ne modifiez pas votre réponse originale, en superposant les votes reçus à votre nouvelle idée.
Jeff Swensen,
1
Je ne suis pas d'accord ... la réponse est toujours assez similaire car il utiliserait la même syntaxe. Non seulement cela, mais si les votants n'apprécient pas la nouvelle réponse, ils peuvent changer leur vote car la réponse a été modifiée. C'est exactement pourquoi vous pouvez modifier votre vote après les modifications. C'est aussi pourquoi j'ai mis "on edit" en gras.
Robert S Ciaccio
4

Il y a deux solutions auxquelles je peux penser:

La manière la plus simple - En utilisant le Info.plistfichier dans chaque Contentsrépertoire .app , créez un index de la valeur pour les clés CFBundleExecutable. Ajoutez ensuite un alias court qui appelle un script (perl, python, peu importe) avec le nom d'un exécutable et les arguments que vous souhaitez transmettre.

Votre index serait constitué de paires de noms d'exécutables et de chemins d'accès à ces exécutables. Vous devez écrire un script planifié récurrent pour le garder à jour.

Vous pourriez finir par appeler:

f foo file.txt

où f est un alias d'un script qui vérifie votre index pour un exécutable nommé foo.

Dans l'ensemble, pas beaucoup de travail pour obtenir les fonctionnalités que vous souhaitez.

La manière la plus difficile - Étendez votre shell pour compléter la gestion de son command_not_founderreur. Essentiellement, vous implémenteriez une method_missingfonctionnalité de style Ruby dans le shell que vous utilisez. Une fois command_not_foundlancé, votre méthode vérifierait les noms des exécutables de vos applications installées.

Jeff Swensen
la source
2

Applescript à la rescousse:

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

Remplacez le nom de l'application par l'application que vous souhaitez démarrer et vous avez terminé. Vous pouvez bien sûr en faire une fonction shell si besoin:

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

et utilisez-le de cette façon:

f iTunes

Je déteste applescript, mais c'est parfois utile, et je crois que la seule façon d'adresser une application simplement par son nom sur la ligne de commande. Tout le reste nécessitera un chemin complet.

eric
la source
Ne prend pas en charge la complétion par tabulation. Nécessite de se souvenir exactement des noms des applications. Je ne vois pas pourquoi AppleScript est requis (surtout si vous le détestez) si les scripts shell peuvent tout faire.
1
Je n'ai jamais réalisé que open -a pouvait être utilisé sans un nom de fichier comme argument. Et j'utilise open depuis NextStep parfois au début des années 90! Veuillez soumettre cela comme réponse afin que je puisse voter dessus. Alors oui, la bonne réponse est d'utiliser 'open -a <nom de l'application>'
eric