Iniciar concisa os aplicativos Mac OS a partir da linha de comando

22

Faço bastante trabalho na linha de comando e me pego definindo muitos aliases do formulário:

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

Existe uma maneira de adicionar magia de tal forma que solicitar o executável "foo" use automaticamente o executável /Applications/Foo.app/Contents/MacOS/Foo? Isso está no OS X 10.6.

(Estou ciente de que poderia fazê-lo open foo.pdf, mas o Skim não é o leitor de PDF padrão e eu gostaria de uma solução geral - em alguns casos, não é apropriado definir o aplicativo em questão como o manipulador padrão do arquivo.)

Reid
fonte
22
Você pode open -a Skim foo.pdf.
1
@mankoff: bom, esqueci que você poderia usar -a para dizer ao open que deseja um aplicativo do diretório Applications. você deve mudar isso para uma resposta :)
Robert S Ciaccio
@ Reid: você poderia editar sua pergunta para incluir um exemplo de linha de comando que deseja digitar?
Robert S Ciaccio
@mankoff que significa que ele precisa aliases com certeza ou ampliação de conclusão de tabulação
Robert S Ciaccio
@Requer algum problema com a solução?

Respostas:

10

Finalmente entendi: Adicione isso ao seu .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()

Agora, o seguinte trabalho:

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

Editar por Reid:

Eu implementei o acima como um script Python que cria scripts de wrapper em vez de aliases. Você precisará colocar ~/bin/mankoffmagicno seu caminho. Se você deseja que os wrappers sejam atualizados automaticamente, execute-o regularmente a partir de cron ou algo assim.

#!/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
fonte
O "grep -v iWork" está lá apenas porque o apóstrofo no "iWork '09" está atrapalhando as coisas. Observe que, se você armazenar aplicativos em outro lugar (~ / local / Applications), basta adicioná-lo ao lscomando, e isso funcionará também.
Agradável. gosto que seja dinâmico, para que você não tenha problemas de manutenção, como limpar aliases antigos.
Robert S Ciaccio
Obrigado @mankoff! Cheira muito bem - depois de testá-lo e funcionar, vou marcar a resposta como aceita. Muito apreciado.
Reid
OK, isso funciona. Ele quebra em aplicativos com um apóstrofo (e presumivelmente outros caracteres engraçados no nome). Pesquisei sua solução e fiz algo semelhante no Python, que postarei em outra resposta - embora, se essa não for a etiqueta correta, fico feliz em adicioná-la à sua resposta (ou o que seja).
Reid
8

Você não precisa de nada extravagante, você já tem a resposta. Experimentar:

open /Applications/Foo.app bar.pdf

na edição

À luz dos comentários abaixo, acho que minha resposta ainda seria relativamente semelhante ... Eu criaria uma função que substitui a abertura, pressiona /Applications, chama /usr/bin/open $appName.app $args, aciona e retorna.

Eu sou péssimo em scripts de shell, mas algo como abaixo, que abrange casos especiais e mantém você usando praticamente a mesma sintaxe que a apple forneceu aberta. Eu gosto de manter meu ambiente o mais limpo possível.

Tenho certeza de que a sintaxe é do espaço sideral:

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
}

na segunda edição

olhando o comentário de @ mankoff sobre a pergunta original, a maioria das coisas na função acima provavelmente seria um desperdício de tempo, já que você poderia apenas usar open -a $appName. Então mankoff provavelmente tem a solução mais fácil e deve mudar seu comentário para uma resposta;)

Robert S Ciaccio
fonte
Eu acho que @Reid quer uma maneira mais curta sem criar manualmente todos os aliases.
Mas ele não precisa criar um alias para o executável do unix se ele usar 'open' no pacote .app. Tive a impressão de que ele estava criando aliases porque achava que a única maneira de iniciar um aplicativo diretamente da linha de comando era ir até o diretório do MacOS e executar o arquivo que é o executável 'real' dentro de um aplicativo. Portanto, nesse caso, um alias salvaria ao menos digitar os três níveis extras da estrutura de diretórios necessários para iniciar o aplicativo. Talvez eu esteja entendendo mal a pergunta dele.
Robert S Ciaccio 23/11
@calevara, obrigado - @mankoff está certo; é sobre comprimento.
Reid
3
Isso é um pouco de mau uso da funcionalidade de edição. Se mais tarde você quiser propor uma segunda resposta, faça exatamente isso. Não edite sua resposta original, pegando os votos recebidos na sua nova ideia.
Jeff Swensen
1
Não concordo ... a resposta ainda é bem parecida, porque ele usaria a mesma sintaxe. Não apenas isso, mas se os eleitores não gostarem da nova resposta, poderão mudar seu voto porque a resposta foi editada. É exatamente por isso que você pode alterar seu voto após as edições. É também por isso que coloquei "em edição" em negrito.
Robert S Ciaccio
4

Existem duas soluções em que posso pensar:

A maneira mais fácil - Usando o Info.plistarquivo em cada Contentsdiretório .app , crie um índice do valor para as chaves CFBundleExecutable. Em seguida, adicione um pequeno apelido que chame um script (perl, python, qualquer que seja) com o nome de um executável e os argumentos que você gostaria de passar.

Seu índice seria pares de nomes de executáveis ​​e caminhos para esses executáveis. Você precisaria escrever um script agendado recorrente para manter isso atualizado.

Você poderia ligar para:

f foo file.txt

onde f é um alias para um script que verifica seu índice em busca de um executável chamado foo.

Em suma, não há muito trabalho para obter a funcionalidade que você deseja.

A maneira mais difícil - Estenda seu shell para complementar a manipulação de seu command_not_founderro. Essencialmente, você implementaria uma method_missingfuncionalidade no estilo Ruby dentro de qualquer shell que estiver usando. Quando command_not_foundlançado, seu método verifica os nomes dos executáveis ​​dos aplicativos instalados.

Jeff Swensen
fonte
2

Applescript para o resgate:

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

Substitua o nome do aplicativo pelo aplicativo que você deseja iniciar e pronto. Obviamente, você pode torná-lo uma função shell, se necessário:

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

e use dessa maneira:

f iTunes

Eu odeio o AppleScript, mas às vezes é útil e acredito que a única maneira de abordar um aplicativo simplesmente pelo nome na linha de comando. Tudo o resto exigirá um caminho completo.

Eric
fonte
Não suporta preenchimento de guias. Requer lembrar exatamente os nomes dos aplicativos. Não veja por que o AppleScript é necessário (especialmente se você odeia) se o script de shell pode fazer tudo.
1
Eu nunca percebi que open -a poderia ser usado sem um nome de arquivo como argumento. E uso open desde NextStep algumas vezes no início dos anos 90! Envie isso como resposta para que eu possa votar. Então, sim, a resposta correta é usar 'open -a <nome do aplicativo>'
eric