Inicio conciso de aplicaciones Mac OS desde la línea de comandos

22

Realizo una buena cantidad de trabajo en la línea de comandos, y me encuentro definiendo muchos alias de la forma:

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

¿Hay alguna manera de agregar magia de tal manera que al pedir un "foo" ejecutable automáticamente use /Applications/Foo.app/Contents/MacOS/Foo ejecutable? Esto está 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).

Reid
fuente
22
Puede open -a Skim foo.pdf.
1
@mankoff: bueno, olvidé que podrías usar -a para decirle a open que quieres una aplicación del directorio de aplicaciones. deberías cambiar esto a una respuesta :)
Robert S Ciaccio
@Reid: ¿podría editar su pregunta para incluir una línea de comando de ejemplo que quiera escribir?
Robert S Ciaccio
@mankoff, lo que significa que necesita un alias seguro o una extensión para completar la pestaña
Robert S Ciaccio
@ ¿Has solucionado problemas con la solución?

Respuestas:

10

Finalmente lo tengo: agregue esto a su .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.

Editar por Reid:

Implementé lo anterior como un script de Python que crea scripts de envoltura en lugar de alias. Tendrás que poner ~/bin/mankoffmagicen tu camino. Si desea que los contenedores se actualicen automáticamente, ejecútelos regularmente desde cron o 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
fuente
El "grep -v iWork" está ahí solo porque el apóstrofe en "iWork '09" está arruinando las cosas. Tenga en cuenta que si almacena aplicaciones en otro lugar (~ / local / Aplicaciones) simplemente agregue eso 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 viejos alias.
Robert S Ciaccio
Gracias @mankoff! Huele genial: una vez que lo haya probado y funcione, marcaré la respuesta aceptada. Muy apreciado.
Reid
OK, esto funciona. Se rompe en aplicaciones con un apóstrofe (y presumiblemente otros caracteres divertidos en el nombre). Analicé 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).
Reid
8

No necesitas nada lujoso, 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 abrir, hace un empuje /Applications, llama /usr/bin/open $appName.app $args, hace un popd y regresa.

Apelo a las secuencias de comandos de shell, pero algo como a continuación que cubre casos especiales y te mantiene usando casi la misma sintaxis que Apple proporciona 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ía una pérdida de tiempo, ya que podría usarlas open -a $appName. Entonces mankoff probablemente tenga la solución más fácil y debería cambiar su comentario a una respuesta;)

Robert S Ciaccio
fuente
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. Me dio la impresión de que estaba creando alias porque pensó que la única forma de iniciar una aplicación directamente desde la línea de comandos era ir al directorio de MacOS y ejecutar el archivo que es el ejecutable 'real' dentro de una aplicación. Entonces, en ese caso, un alias ahorraría al menos escribir los tres niveles adicionales de estructura de directorios que necesitaría para iniciar la aplicación. Tal vez estoy malinterpretando su pregunta.
Robert S Ciaccio
@calevara, gracias - @mankoff tiene razón; Se trata de longitud.
Reid
3
Esto es un mal uso de la funcionalidad de edición. Si luego desea proponer una segunda respuesta, haga exactamente eso. No edite su respuesta original, aprovechando los votos que recibió sobre su nueva idea.
Jeff Swensen el
1
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 no les gusta la nueva respuesta, pueden cambiar su voto porque la respuesta ha sido editada. Eso es exactamente por qué puede cambiar su voto después de las ediciones. También es por qué pongo "editar" en negrita.
Robert S Ciaccio
4

Hay dos soluciones en las que puedo pensar:

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ía pares de nombres ejecutables y rutas a esos ejecutables. Tendría que escribir un script programado recurrente para mantener esto actualizado.

Terminarías pudiendo llamar:

f foo file.txt

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

En general, no hay mucho trabajo para obtener la funcionalidad que desea.

La forma más difícil : amplíe su shell para complementar el manejo de su command_not_founderror. Esencialmente, implementaría una method_missingfuncionalidad de estilo Ruby dentro de cualquier shell que esté utilizando. Cuando command_not_foundse lanzó, su método verificaría los nombres ejecutables de sus aplicaciones instaladas.

Jeff Swensen
fuente
2

Apéndice 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 ya está. 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 úsalo de esa manera:

f iTunes

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

eric
fuente
No es compatible con la finalización de pestañas. Requiere recordar los nombres de las aplicaciones exactamente. No veo por qué se requiere AppleScript (especialmente si lo odias) si las secuencias de comandos de shell pueden hacerlo todo.
1
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>'
eric