Secara ringkas memulai aplikasi Mac OS dari baris perintah

22

Saya melakukan cukup banyak pekerjaan di baris perintah, dan saya mendapati diri saya mendefinisikan banyak alias dalam bentuk:

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

Apakah ada cara untuk menambahkan sihir sehingga meminta "foo" yang dapat dieksekusi secara otomatis menggunakan executable /Applications/Foo.app/Contents/MacOS/Foo? Ini ada di OS X 10.6.

(Saya sadar bahwa saya bisa melakukannya open foo.pdf, tetapi Skim bukan pembaca PDF default, dan saya ingin solusi umum - dalam beberapa kasus, tidak tepat untuk mengatur aplikasi yang dimaksud sebagai pengendali default untuk file tersebut.)

Reid
sumber
22
Kamu bisa open -a Skim foo.pdf.
1
@mankoff: bagus, saya lupa Anda bisa menggunakan -a untuk memberi tahu bahwa Anda menginginkan aplikasi dari dir Aplikasi. Anda harus mengubah ini menjadi jawaban :)
Robert S Ciaccio
@ Reid: bisakah Anda mengedit pertanyaan Anda untuk menyertakan contoh baris perintah yang ingin Anda ketikkan?
Robert S Ciaccio
@mankoff yang berarti dia membutuhkan alias untuk kepastian atau perpanjangan penyelesaian tab
Robert S Ciaccio
@Apakah ada masalah dengan solusinya?

Jawaban:

10

Akhirnya saya mengerti: Tambahkan ini ke .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()

Sekarang pekerjaan berikut:

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

Edit oleh Reid:

Saya menerapkan di atas sebagai skrip Python yang membuat skrip wrapper alih-alih alias. Anda harus menempatkan ~/bin/mankoffmagicjalan Anda. Jika Anda ingin pembungkus diperbarui secara otomatis, jalankan secara teratur dari cron atau semacamnya.

#!/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
sumber
"Grep -v iWork" ada di sana hanya karena tanda kutip di "iWork '09" mengacaukan segalanya. Perhatikan bahwa jika Anda menyimpan aplikasi di tempat lain (~ / local / Applications) tambahkan saja ke lsperintah, dan ini juga akan berfungsi di sana.
bagus Saya suka itu dinamis sehingga Anda tidak berakhir dengan masalah pemeliharaan seperti membersihkan alias lama.
Robert S Ciaccio
Terima kasih @mankoff! Baunya luar biasa - begitu saya mengujinya dan berfungsi saya akan menandai jawaban yang diterima. Sangat dihargai.
Reid
Oke, ini berhasil. Itu pecah pada aplikasi dengan tanda kutip (dan mungkin karakter lucu lainnya dalam nama). Saya riff pada solusi Anda dan melakukan sesuatu yang serupa dengan Python, yang saya akan posting di jawaban lain - meskipun jika itu bukan etiket yang tepat, saya senang menambahkannya ke jawaban Anda sebagai gantinya (atau apa pun).
Reid
8

Anda tidak perlu sesuatu yang mewah, Anda sudah memiliki jawabannya. Mencoba:

open /Applications/Foo.app bar.pdf

di sunting

Mengingat komentar di bawah ini, saya pikir jawaban saya masih akan relatif sama ... Saya akan membuat fungsi yang menimpa terbuka, melakukan pushd to /Applications, panggilan /usr/bin/open $appName.app $args, melakukan popd dan kembali.

Saya payah di skrip shell tetapi sesuatu seperti di bawah ini yang mencakup kasus-kasus khusus dan membuat Anda menggunakan sintaks yang sama seperti apel yang disediakan untuk terbuka. Saya suka menjaga lingkungan saya sebersih mungkin.

Saya yakin sintaksnya dari luar angkasa:

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
}

pada edit kedua

melihat komentar @ mankoff pada pertanyaan awal, sebagian besar hal dalam fungsi di atas mungkin akan membuang-buang waktu karena Anda hanya bisa menggunakan open -a $appName. Jadi mankoff mungkin memiliki solusi termudah dan harus mengubah komentarnya menjadi jawaban;)

Robert S Ciaccio
sumber
Saya pikir @ Reid ingin cara yang lebih pendek tanpa membuat secara manual semua alias.
Tapi dia tidak harus membuat alias ke executable unix jika dia menggunakan 'open' pada paket .app. Saya mendapat kesan bahwa dia membuat alias karena dia pikir satu-satunya cara untuk memulai aplikasi langsung dari baris perintah adalah dengan masuk ke direktori MacOS dan mengeksekusi file yang dapat dieksekusi 'nyata' di dalam aplikasi. Jadi dalam hal itu, alias setidaknya akan menyimpan mengetik tiga tingkat struktur direktori tambahan yang Anda perlukan untuk memulai aplikasi. Mungkin saya salah paham pertanyaannya.
Robert S Ciaccio
@calevara, terima kasih - @mankoff benar; ini tentang panjang.
Reid
3
Ini sedikit penyalahgunaan fungsi edit. Jika nanti Anda ingin mengajukan jawaban kedua, lakukan saja. Jangan mengedit jawaban asli Anda, membonceng suara yang diterima ke ide baru Anda.
Jeff Swensen
1
Saya tidak setuju ... jawabannya masih sangat mirip karena dia akan menggunakan sintaks yang sama. Bukan hanya itu, tetapi jika pemilih tidak menyukai jawaban baru mereka dapat mengubah suara mereka karena jawaban telah diedit. Itulah mengapa Anda dapat mengubah suara setelah diedit. Itu juga sebabnya saya menuliskan "sunting" dengan huruf tebal.
Robert S Ciaccio
4

Ada dua solusi yang dapat saya pikirkan:

Cara yang lebih mudah - Menggunakan Info.plistfile di setiap Contentsdirektori .app , buat indeks nilai untuk kunci CFBundleExecutable. Kemudian tambahkan alias pendek yang memanggil skrip (perl, python, apa pun) dengan nama yang dapat dieksekusi dan argumen yang ingin Anda sampaikan.

Indeks Anda akan menjadi pasangan nama yang dapat dieksekusi dan jalur ke yang dapat dieksekusi tersebut. Anda harus menulis skrip terjadwal berulang untuk menjaga ini diperbarui.

Anda akhirnya bisa menelepon:

f foo file.txt

di mana f adalah alias untuk skrip yang memeriksa indeks Anda untuk nama yang dapat dieksekusi foo.

Secara keseluruhan, tidak banyak pekerjaan untuk mendapatkan fungsionalitas yang Anda inginkan.

Cara yang lebih sulit - Perluas shell Anda untuk melengkapi penanganan command_not_foundkesalahannya. Pada dasarnya Anda akan menerapkan method_missingfungsionalitas gaya Ruby di dalam shell apa pun yang Anda gunakan. Ketika command_not_founddilempar, metode Anda akan memeriksa nama yang dapat dieksekusi dari Aplikasi yang Anda instal.

Jeff Swensen
sumber
2

Tepuk tangan untuk menyelamatkan:

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

Ganti nama aplikasi dengan aplikasi yang ingin Anda mulai dan Anda selesai. Anda tentu saja dapat menjadikannya fungsi shell jika diperlukan:

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

dan gunakan seperti itu:

f iTunes

Saya benci applescript, tetapi kadang-kadang berguna, dan saya percaya satu-satunya cara untuk mengatasi aplikasi hanya dengan nama di baris perintah. Yang lainnya akan membutuhkan jalur penuh.

eric
sumber
Tidak mendukung penyelesaian tab. Membutuhkan mengingat nama aplikasi dengan tepat. Tidak mengerti mengapa AppleScript diperlukan (terutama jika Anda membencinya) jika skrip shell dapat melakukan semuanya.
1
Saya tidak pernah menyadari bahwa open -a dapat digunakan tanpa nama file sebagai argumen. Dan saya telah menggunakan buka sejak NextStep kadang-kadang di awal 90-an! Harap kirimkan itu sebagai jawaban agar saya dapat memilihnya. Jadi ya, jawaban yang benar adalah dengan menggunakan 'open -a <application name>'
eric