31

I can launch an xterm from the command line (or a program, via a system call) like so:

/usr/X11/bin/xterm -fg SkyBlue -bg black -e myscript

That will launch an xterm with blue text and a black background, and run an arbitrary script inside it.

My question: How do I do the equivalent with Terminal.app?

dreeves
  • 26,430
  • 45
  • 154
  • 229
  • I suppose one way to do this would be to create multiple versions of ~/Library/Preferences/com.apple.Terminal.plist and then launch Terminal with the appropriate one. Can the plist file be specified on the command line? – dreeves Dec 10 '10 at 03:24

6 Answers6

22

You can open an app by bundle id too, and give other parameters.

If there's an executable script test.sh in the current directory, the following command will open and run it in Terminal.app

 open -b com.apple.terminal test.sh 

The only down side that I can find is that Terminal doesn't appear to inherit your current environment, so you'll have to arrange another way to pass parameters through to the script that you want to run. I guess building the script on the fly to embed the parameters would be one approach (taking into account the security implications of course...)

Sam Deane
  • 1,553
  • 1
  • 13
  • 17
  • 2
    This doesn't directly address the colour question, but it's a better way to launch an existing shell script in a Terminal window. – Sam Deane Oct 19 '11 at 11:32
15

Assuming you already have the colors you want in one of your Terminal profiles, here's what I came up with (with some help from Juha's answer and from this Serverfault answer).


Update:

On reflection, I think this echo business is too complicated. It turns out you can use osascript to make an executable AppleScript file with a shebang line:

#!/usr/bin/osascript                                                                            

on run argv                                                                                     
  if length of argv is equal to 0                                                               
    set command to ""                                                                           
  else                                                                                          
    set command to item 1 of argv                                                               
  end if                                                                                        

  if length of argv is greater than 1                                                           
    set profile to item 2 of argv                                                               
    runWithProfile(command, profile)                                                            
  else                                                                                          
    runSimple(command)                                                                          
  end if                                                                                        
end run                                                                                         

on runSimple(command)                                                                           
  tell application "Terminal"                                                                   
    activate                                                                                    
    set newTab to do script(command)                                                            
  end tell                                                                                      
  return newTab                                                                                 
end runSimple                                                                                   

on runWithProfile(command, profile)                                                             
  set newTab to runSimple(command)                                                              
  tell application "Terminal" to set current settings of newTab to (first settings set whose name is profile)                                                                                      
end runWithProfile

Save that as term.scpt, make it executable with chmod +x, and use it the same way as below, e.g. term.scpt "emacs -nw" "Red Sands".


Original answer:

Assuming we save the script below as term.sh...

#!/bin/sh

echo '
on run argv
  if length of argv is equal to 0
    set command to ""
  else
    set command to item 1 of argv
  end if

  if length of argv is greater than 1
    set profile to item 2 of argv
    runWithProfile(command, profile)
  else
    runSimple(command)
  end if
end run

on runSimple(command)
  tell application "Terminal"
    activate
    set newTab to do script(command)
  end tell
  return newTab
end runSimple

on runWithProfile(command, profile)
  set newTab to runSimple(command)
  tell application "Terminal" to set current settings of newTab to (first settings set whose name is profile)
end runWithProfile
' | osascript - "$@" > /dev/null

...it can be invoked as follows:

  • term.sh
    • opens a new terminal window, nothing special
  • term.sh COMMAND
    • opens a new terminal window, executing the specified command. Commands with arguments can be enclosed in quotes, e.g. term.sh "emacs -nw" to open a new terminal and run (non-windowed) emacs
  • term.sh COMMAND PROFILE
    • opens a new terminal window, executing the specified command, and sets it to the specified profile. Profiles with spaces in their names can be enclosed in quotes, e.g. term.sh "emacs -nw" "Red Sands" to open a new terminal and run (non-windowed) emacs with the Red Sands profile.

If you invoke it with a bad command name, it'll still open the window and set the profile, but you'll get bash's error message in the new window.

If you invoke it with a bad profile name, the window will still open and the command will still execute but the window will stick with the default profile and you'll get an error message (to stderr wherever you launched it) along the lines of

525:601: execution error: Terminal got an error: Can’t get settings set 1 whose name = "elvis". Invalid index. (-1719)

The invocation is slightly hacky, and could probably be improved if I took the time to learn getopt (e.g., something like term.sh -p profile -e command would be better and would, for instance, allow you to easily open a new terminal in the specified profile without invoking a command). And I also wouldn't be surprised if there are ways to screw it up with complex quoting. But it works for most purposes.

David Moles
  • 48,006
  • 27
  • 136
  • 235
  • 1
    Works! I've been waiting for this for 4 years. :) Thank you. – dreeves Feb 07 '15 at 23:55
  • Thanks for the script! Unfortunately in my computer it always opens 2 Terminal windows if Terminal is not already active. Any ideas how to avoid this? – Aivar Jul 13 '16 at 19:08
  • @Aivar Hmm, I'm not sure. My guess is that it has something to do with either `activate` or `do script()`, but I'm not a Terminal / Applescript expert. – David Moles Jul 13 '16 at 20:59
  • This does not start the process in terminal in the current working directory where one launches term.scpt. used the first form (with bash) and added ` set command to "cd '$PWD' ;" & command` so sh can insert the $PWD. This won't work if PWD contains spaces, but a fix is to rejigger the quoting for sh. I'll post another answer with that. – djb Jan 31 '18 at 19:38
  • 1
    I need to open numerous terminals to SSH into different machines for wrk everyday and I'm profile picky... Thanks for this; saves me all kinds of time! – freddythunder Nov 30 '18 at 16:06
  • can't get this to work - do script always opens a new terminal window and does nothing, no matter what I set as command - using Catalina – Rhubarb Jul 13 '20 at 11:09
  • @Rhubarb I don't know what to tell you. :( I haven't actually been using it for some time but I just tried it on a week-old Catalina install and it works for me. My script is in [this gist](https://gist.github.com/dmoles/b660afc8437ff35503f7de34a023fbd1) if you want to make sure there's no typos. Otherwise, my guess is a Catalina security problem, maybe under "Automation"? – David Moles Jul 13 '20 at 18:46
14

Almost all (every?) osx program can be launched from command line using:

appName.app/Contents/MacOS/command

For terminal the command is:

/Applications/Utilities/Terminal.app/Contents/MacOS/Terminal

You can use the autocomplete (tab) or ls to find the correct filenames. ".app" is basically a folder.

To change the colors and run a script... I think you cannot do it with shell scripts as Terminal does not accept arguments ("Terminal myScript.sh" does not launch myScript). With iTerm this works.

Workaround is to use applescript (wrapped in a shell script):

   #!/bin/sh
   osascript -e '
     tell application "Terminal"
       activate
       tell window 1
          do script "sleep 5; exit"
          set background color to {0, 11111, 11111}
          set win_id to id
       end tell

       set w_ids to (id of every window)

       repeat while w_ids contains win_id
         delay 1
         set w_ids to (id of every window)
       end repeat
    end tell'

Ok, now it should behave exactly the same as the xterm example. The drawback is the constant polling of the window ids (which is bad programming).

edit: A bit more elegant applescript would use the 'busy' property of Terminal. I will leave the original code as is works for a general program (not just terminal).

tell application "Terminal"
    tell window 1
        do script "sleep 2"
        set background color to {0, 11111, 11111}
        repeat while busy
            delay 1
        end repeat
        close
    end tell
end tell

Also for perfectly correct program, one should check that whether the terminal is running or not. It affects the number of windows opened. So, this should be run first (again a nasty looking hack, that I will edit later as I find a working solution).

tell application "System Events"
    if (count (processes whose name is "Terminal")) is 0 then
        tell application "Terminal"
            tell window 1
                close
            end tell
        end tell
    end if
end tell

br,
Juha

Juha
  • 2,053
  • 23
  • 44
  • One difference I just found between this and the xterm equivalent: calling this shell script returns immediately, not waiting for the command ("echo boo" in your example) to finish. Do you know how to make it wait? Thanks again! – dreeves Dec 11 '10 at 10:28
  • @dreeves: Ah, you can change "script" to "shell script" and drop the "with command". I edit the answer a bit... – Juha Dec 13 '10 at 11:31
  • @Juha: Thanks for the fix! I'm within epsilon now and can probably figure this out for myself but do you know offhand how to prevent the script from outputting the list of window ids? (I'm also thinking this may all be a bit too ugly and I should suck it up and make a proper Cocoa app rather than all this hackery with launching scripts in Terminals!) – dreeves Dec 16 '10 at 00:47
  • I added an alternative script that is more elegant (does not use ids). – Juha Dec 22 '10 at 11:22
  • Thanks so much for all the help with this! The new version actually isn't working for me at all. The window opens and closes immediately. The script I'm using where you have "sleep 2" waits for user input on stdin. Maybe that doesn't respond as expected to the "delay while busy" check? – dreeves Dec 24 '10 at 18:01
  • Where can we find documentation on the available `tell` commands for Terminal? – Adam Mar 19 '14 at 23:08
  • Open applescript editor then File-> Open dictionary... and choose terminal from the list. It opens a window where you can for example navigate to: terminal suite -> settings set. Here you can find settings that you can modify like background color, cursor color, etc. – Juha Mar 21 '14 at 19:48
  • Quoting David Moles who just improved upon this answer: This answer pretty much solves this (thanks Juha!) but prints some extraneous stuff (the list of window IDs) to stdout. Also, as Juha points out, it's using an ugly hack in continuously polling all the Terminal windows. – dreeves Feb 07 '15 at 23:59
4

You can also go into terminal GUI, completely configure the options to your heart's content, and export them to a ".terminal" file, and/or group the configurations into a Window Group and export that to a terminal file "myGroup.terminal". Then

open myGroup.terminal

will open the terminal(s) at once, with all your settings and startup commands as configured.

jerseyboy
  • 1,298
  • 13
  • 13
  • On Sierra, the Shell -> Export Settings ... method appears to save the default settings but it does create a new profile with the same name as the file you created. Alternatively, you can do Terminal -> Preferences -> Profiles. Create a profile under a name and then use the gear menu => Export ... to export the profile that you are currently mucking with. – pedz Dec 24 '16 at 21:53
  • If you change the name as you save it, yet another profile will be created but it will use the settings you want. You can even delete these profiles but when you run the command, it automagically adds the profiles back. There seems to be a lot of oddness in how all this "works". – pedz Dec 24 '16 at 21:56
1

you can launch terminal with the following command, not sure how to specify colors:

 open /Applications/Utilities/Terminal.app/
jhuebsch
  • 451
  • 2
  • 8
  • 2
    For newer OS X versions, you will need to specify the "-a" argument to signify the name of the app. "open -n -a /Applications/Utilities/Terminal.app/" Also, the "-n" argument may be helpful to start a new window (optional). – Neil Monroe Dec 11 '14 at 16:37
1

The answer from @david-moles above works but run the terminal and command in ~ rather the current working directory where term was launched. This variation adds a cd command.

#!/usr/bin/env bash
# based on answer by @david-moles in 
# https://stackoverflow.com/questions/4404242/programmatically-launch-terminal-app-with-a-specified-command-and-custom-colors
echo "
on run argv
  if length of argv is equal to 0
    set command to \"\"
  else
    set command to item 1 of argv
  end if
  set command to \"cd '"$PWD"' ;\" & command
  if length of argv is greater than 1
    set profile to item 2 of argv
    runWithProfile(command, profile)
  else
    runSimple(command)
  end if
end run

on runSimple(command)
  tell application \"Terminal\"
    activate
    set newTab to do script(command)
  end tell
  return newTab
end runSimple

on runWithProfile(command, profile)
  set newTab to runSimple(command)
  tell application \"Terminal\" to set current settings of newTab to (first settings set whose name is profile)
end runWithProfile
" | osascript - "$@" > /dev/null

There may be a way to set PWD this with applescript.

Note: When I use this, I sometimes two Terminal windows, one a shell running in ~ and a second which runs the cd command and command from argv[1]. Seems to happen if Terminal is not already running; perhaps it is opening old state (even tho I had no open terminals when I closed it).

djb
  • 4,930
  • 1
  • 34
  • 37