Iniciar aplicaciones de Mac OS de forma concisa desde la línea de comandos

Hago una buena cantidad de trabajo en la línea de comando, y me encuentro definiendo muchos alias del formulario:

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

¿Hay alguna manera de agregar magia de modo que solicitar el ejecutable "foo" use automáticamente el ejecutable /Applications/Foo.app/Contents/MacOS/Foo? Esto es en OS X 10.6.

(Soy consciente de que podría hacerlo open foo.pdf, pero Skim no es el lector de PDF predeterminado y me gustaría una solución general; en algunos casos, no es apropiado configurar la aplicación en cuestión como el controlador predeterminado para el archivo).

open -a Skim foo.pdftu puedes
@mankoff: bien, olvidé que podrías usar -a para decirle a open que quieres una aplicación del directorio Aplicaciones. deberías cambiar esto a una respuesta :)
@Reid: ¿podría editar su pregunta para incluir una línea de comando de ejemplo que desea poder escribir?
@mankoff, lo que significa que necesita alias con seguridad o extensión de la finalización de la pestaña
@Reid ¿algún problema con la solución?

Respuestas (4)

Finalmente lo tengo: Agrega esto a tu.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()

Ahora el siguiente trabajo:

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

Editado por Reid:

Implementé lo anterior como un script de Python que crea scripts de envoltura en lugar de alias. Tendrás que ponerte ~/bin/mankoffmagicen tu camino. Si desea que los envoltorios se actualicen automáticamente, ejecútelos regularmente desde cron o algo así.

#!/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()
El "grep -v iWork" está ahí solo porque el apóstrofe en "iWork '09" está estropeando las cosas. Tenga en cuenta que si almacena aplicaciones en otro lugar (~/local/Applications), simplemente agréguelo al lscomando, y esto también funcionará allí.
Buena esa. Me gusta que sea dinámico para que no termines con ningún problema de mantenimiento, como limpiar alias antiguos.
Gracias @mankoff! Huele muy bien: una vez que lo haya probado y funcione, marcaré la respuesta como aceptada. Muy apreciado.
Bien, esto funciona. Se rompe en las aplicaciones con un apóstrofe (y presumiblemente otros caracteres divertidos en el nombre). Hice un riff en su solución e hice algo similar en Python, que publicaré en otra respuesta, aunque si esa no es la etiqueta correcta, me complace agregarla a su respuesta (o lo que sea).

No necesitas nada sofisticado, ya tienes la respuesta. Tratar:

open /Applications/Foo.app bar.pdf

en editar

A la luz de los comentarios a continuación, creo que mi respuesta aún sería relativamente similar ... Haría una función que anula open, hace un pushd a /Applications, llama /usr/bin/open $appName.app $args, hace un popd y regresa.

Apesto en las secuencias de comandos de shell, pero algo como a continuación, que cubre casos especiales y lo mantiene usando prácticamente la misma sintaxis que Apple proporcionó para abrir. Me gusta mantener mi entorno lo más limpio posible.

Estoy seguro de que la sintaxis es del espacio exterior:

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
}

en la segunda edición

mirando el comentario de @mankoff sobre la pregunta original, la mayoría de las cosas en la función anterior probablemente serían una pérdida de tiempo ya que solo podría usar open -a $appName. Entonces mankoff probablemente tenga la solución más fácil y debería cambiar su comentario a una respuesta;)

Creo que @Reid quiere una forma más corta sin crear manualmente todos los alias.
Pero no tiene que crear un alias para el ejecutable de Unix si usa 'abrir' en el paquete .app. Tuve la impresión de que estaba creando alias porque pensaba que la única forma de iniciar una aplicación directamente desde la línea de comandos era yendo hasta el directorio de MacOS y ejecutando el archivo que es el ejecutable 'real' dentro de una aplicación. Entonces, en ese caso, un alias al menos ahorraría escribir los tres niveles adicionales de estructura de directorio que necesitaría para iniciar la aplicación. Tal vez estoy malinterpretando su pregunta.
@calevara, gracias - @mankoff tiene razón; se trata de longitud.
ok, si entiendo correctamente ahora, ¿desea simplemente escribir el nombre de la aplicación como si fuera una utilidad de línea de comando en su camino? entonces $ foo bar.pdf?
Este es un mal uso de la función de edición. Si más tarde desea proponer una segunda respuesta, hágalo. No edite su respuesta original, aprovechando los votos que recibió en su nueva idea.
No estoy de acuerdo... la respuesta sigue siendo bastante similar porque estaría usando la misma sintaxis. No solo eso, sino que si a los votantes positivos no les gusta la nueva respuesta, pueden cambiar su voto porque la respuesta ha sido editada. Es exactamente por eso que puede cambiar su voto después de las ediciones. También es por eso que puse "en edición" en negrita.
@mankoff: estoy de acuerdo, su respuesta es la mejor respuesta y es la solución real. Sin embargo, no puedo controlar la votación de la gente, tal vez algunos simplemente no entienden lo que se está haciendo aquí, o están votando en protesta por los comentarios imbéciles de cierto comentarista. En cualquier caso, se trata realmente de ayudar a la persona que hizo la pregunta y realmente no me importan mucho los votos, siempre que el autor de la pregunta obtenga lo que necesita. Con suerte, Reid encontrará su respuesta útil y elegirá su respuesta como aceptada. Si te sirve de consuelo, uno de tus votos es mío :)
y de todos modos, en general, creo que el hecho de que esta pregunta tenga 4 respuestas con ~20 votos combinados entre ella y las respuestas es una buena señal para esta comunidad. demasiadas preguntas solo tienen 1 o 2 respuestas y 4 o 5 votos en total.

Hay dos soluciones que se me ocurren:

La forma más fácil : utilizando el Info.plistarchivo en cada Contentsdirectorio .app, cree un índice del valor para las claves CFBundleExecutable. Luego agregue un alias corto que llame a un script (perl, python, lo que sea) con el nombre de un ejecutable y los argumentos que le gustaría pasar.

Su índice serían pares de nombres de ejecutables y rutas a esos ejecutables. Tendría que escribir un script programado recurrente para mantener esto actualizado.

Terminarías siendo capaz de llamar:

f foo file.txt

donde f es un alias para un script que verifica su índice en busca de un ejecutable llamado foo.

Considerándolo todo, no es mucho trabajo obtener la funcionalidad que desea.

La forma más difícil : extienda su caparazón para complementar el manejo de su command_not_founderror. Esencialmente, implementaría una funcionalidad de estilo Ruby method_missingdentro de cualquier shell que esté usando. Cuando command_not_foundse lanzó, su método verificaría los nombres ejecutables de sus aplicaciones instaladas.

Applescript al rescate:

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

Reemplace el nombre de la aplicación con la aplicación que desea iniciar y listo. Por supuesto, puede convertirlo en una función de shell si es necesario:

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

y usarlo de esa manera:

f iTunes

Odio AppleScript, pero a veces es útil, y creo que la única forma de abordar una aplicación es simplemente por su nombre en la línea de comandos. Todo lo demás requerirá una ruta completa.

No es compatible con la finalización de pestañas. Requiere recordar exactamente los nombres de las aplicaciones. No veo por qué se requiere AppleScript (especialmente si lo odia) si las secuencias de comandos de Shell pueden hacerlo todo.
Nunca me di cuenta de que open -a podría usarse sin un nombre de archivo como argumento. ¡Y he estado usando Open desde NextStep a veces a principios de los 90! Envíe eso como respuesta para que pueda votarlo. Entonces sí, la respuesta correcta es usar 'open -a <nombre de la aplicación>'