100

I've been trying to figure out how to run a bash command in a new Max OS X Terminal.app window. As, an example, here's how I would run my command in a new bash process:

bash -c "my command here"

But this reuses the existing terminal window instead of creating a new one. I want something like:

Terminal.app -c "my command here"

But of course this doesn't work. I am aware of the "open -a Terminal.app" command, but I don't see how to forward arguments to the terminal, or even if I did what arguments to use.

3
  • 1
    You could always open preferences, goto the "Profiles" tab, go into the "Shell" page and set the startup command there. It only runs when the application is opened, but it works better than the hacky alternatives! Commented Mar 21, 2015 at 15:17
  • Also see the same question at superuser superuser.com/q/174576/122841
    – Beetle
    Commented Oct 11, 2017 at 11:09
  • Many beginners think they want this, until they learn how to run and manipulate background processes. Having a command run in the background with output to a file lets you examine the output file from any terminal, and elegantly scales to many more processes than you can usefully keep track of in simultaneously open interactive but read-only terminal windows.
    – tripleee
    Commented Oct 2, 2023 at 17:50

11 Answers 11

109

one way I can think to do it off the top of my head is to create a .command file and run it like so:

echo echo hello > sayhi.command; chmod +x sayhi.command; open sayhi.command

or use applescript:

osascript -e 'tell application "Terminal" to do script "echo hello"'

although you'll either have to escape a lot of double quotes or not be able to use single quotes

4
  • I've decided on the first method. It's pretty hack-ish, but it works and I don't have to worry about escaping quotes in my command or anything. Thanks.
    – Walt D
    Commented Jun 13, 2009 at 14:19
  • 5
    If the command needs to run parametrized, perhaps you'll want to swap the single and double quotes; though you need to understand the difference in order to be able to do this correctly. osascript -e "tell application \"Terminal\" to do script \"echo '$variable'\"'
    – tripleee
    Commented Dec 6, 2016 at 10:21
  • anyone know how to close the stupid left over terminal window? Commented Aug 25, 2017 at 16:37
  • Add ; exit at the end of the shell script commands, like do script "echo hello; exit". You still need to close the window separately.
    – tripleee
    Commented Jun 18, 2019 at 6:46
68

Partial solution:

Put the things you want done in a shell-script, like so

#!/bin/bash
ls
echo "yey!"

And don't forget to 'chmod +x file' to make it executable. Then you can

open -a Terminal.app scriptfile

and it will run in a new window. Add 'bash' at the end of the script to keep the new session from exiting. (Although you might have to figure out how to load the users rc-files and stuff..)

2
  • 7
    The context of the newly opened window seems to be the user's root folder /Users/{username}. Is there any way to keep the context folder the same as the parent terminal window that opened it? Commented Oct 23, 2015 at 15:28
  • 1
    > Although you might have to figure out how to load the users rc-files and stuff.. use bash -l for this
    – Aivar
    Commented Feb 23, 2017 at 12:32
35

I've been trying to do this for a while. Here is a script that changes to the same working directory, runs the command, and closes the terminal window.

#!/bin/sh 
osascript <<END 
tell application "Terminal"
    do script "cd \"`pwd`\";$1;exit"
end tell
END
3
  • Dislike accepted script, your one solve current directory problem. Thanks!
    – Marboni
    Commented May 30, 2012 at 5:48
  • Great solution; it outputs something like tab 1 of window id 7433 to stdout in the launching shell, though. To suppress that, place >/dev/null before <<END.
    – mklement0
    Commented Aug 4, 2012 at 2:21
  • 1
    This doesn't inherently close the terminal window. It just exits the command interpreter. Terminal.app has to be configured to close the window automatically, or on clean exit of command interpreter.
    – user66001
    Commented Jan 18, 2013 at 7:14
8

In case anyone cares, here's an equivalent for iTerm:

#!/bin/sh
osascript <<END
tell application "iTerm"
 tell the first terminal
  launch session "Default Session"
  tell the last session
   write text "cd \"`pwd`\";$1;exit"
  end tell
 end tell
end tell
END
5

Here's yet another take on it (also using AppleScript):

function newincmd() { 
   declare args 
   # escape single & double quotes 
   args="${@//\'/\'}" 
   args="${args//\"/\\\"}" 
   printf "%s" "${args}" | /usr/bin/pbcopy 
   #printf "%q" "${args}" | /usr/bin/pbcopy 
   /usr/bin/open -a Terminal 
   /usr/bin/osascript -e 'tell application "Terminal" to do script with command "/usr/bin/clear; eval \"$(/usr/bin/pbpaste)\""' 
   return 0 
} 

newincmd ls 

newincmd echo "hello \" world" 
newincmd echo $'hello \' world' 

see: codesnippets.joyent.com/posts/show/1516

1
  • This was the only one that worked for me. My need was that I needed to run a command that would create an ssh tunnel to a machine, and then after that I wanted to open a vncloc to screen share it. I could do it perfectly in a new bash function in 3 steps: tunnel command in new window, sleep 3 seconds, then open the vncloc in original window. Worked perfectly.
    – Tim Fuqua
    Commented May 4, 2021 at 16:35
3

Here's my awesome script, it creates a new terminal window if needed and switches to the directory Finder is in if Finder is frontmost. It has all the machinery you need to run commands.

on run
    -- Figure out if we want to do the cd (doIt)
    -- Figure out what the path is and quote it (myPath)
    try
        tell application "Finder" to set doIt to frontmost
        set myPath to finder_path()
        if myPath is equal to "" then
            set doIt to false
        else
            set myPath to quote_for_bash(myPath)
        end if
    on error
        set doIt to false
    end try

    -- Figure out if we need to open a window
    -- If Terminal was not running, one will be opened automatically
    tell application "System Events" to set isRunning to (exists process "Terminal")

    tell application "Terminal"
        -- Open a new window
        if isRunning then do script ""
        activate
        -- cd to the path
        if doIt then
            -- We need to delay, terminal ignores the second do script otherwise
            delay 0.3
            do script " cd " & myPath in front window
        end if
    end tell
end run

on finder_path()
    try
        tell application "Finder" to set the source_folder to (folder of the front window) as alias
        set thePath to (POSIX path of the source_folder as string)
    on error -- no open folder windows
        set thePath to ""
    end try

    return thePath
end finder_path

-- This simply quotes all occurrences of ' and puts the whole thing between 's
on quote_for_bash(theString)
    set oldDelims to AppleScript's text item delimiters
    set AppleScript's text item delimiters to "'"
    set the parsedList to every text item of theString
    set AppleScript's text item delimiters to "'\\''"
    set theString to the parsedList as string
    set AppleScript's text item delimiters to oldDelims
    return "'" & theString & "'"
end quote_for_bash
3

I made a function version of Oscar's answer, this one also copies the environment and changes to the appropriate directory

function new_window {
    TMP_FILE=$(mktemp "/tmp/command.XXXXXX")
    echo "#!/usr/bin/env bash" > $TMP_FILE

    # Copy over environment (including functions), but filter out readonly stuff
    set | grep -v "\(BASH_VERSINFO\|EUID\|PPID\|SHELLOPTS\|UID\)" >> $TMP_FILE

    # Copy over exported envrionment
    export -p >> $TMP_FILE

    # Change to directory
    echo "cd $(pwd)" >> $TMP_FILE

    # Copy over target command line
    echo "$@" >> $TMP_FILE

    chmod +x "$TMP_FILE"
    open -b com.apple.terminal "$TMP_FILE"

    sleep .1 # Wait for terminal to start
    rm "$TMP_FILE"
}

You can use it like this:

new_window my command here

or

new_window ssh example.com
1
  • 1
    The line TMP_FILE="tmp.command" could be a problem if you start more than one process simultaneously. I would suggest to replace it by TMP_FILE=$(mktemp "/tmp/command.XXXXXX")
    – duthen
    Commented Jun 24, 2017 at 20:40
2

A colleague asked me how to open A LOT of ssh sessions at once. I used cobbal's answer to write this script:

tmpdir=$( mktemp -d )
trap '$DEBUG rm -rf $tmpdir ' EXIT
index=1

{
cat <<COMMANDS
ssh user1@host1
ssh user2@host2
COMMANDS
} | while read command
do 
  COMMAND_FILE=$tmpdir/$index.command
  index=$(( index + 1 ))
  echo $command > $COMMAND_FILE
  chmod +x  $COMMAND_FILE
  open $COMMAND_FILE
done
sleep 60

By updating the list of commands ( they don't have to be ssh invocations ), you will get an additional open window for every command executed. The sleep 60 at the end is there to keep the .command files around while they are being executed. Otherwise, the shell completes too quickly, executing the trap to delete the temp directory ( created by mktemp ) before the launched sessions have an opportunity to read the files.

2

Another option that needs to be here is "ttab"

https://www.npmjs.com/package/ttab

Install
npm install ttab -g
Usage : Open another tab and run ls
ttab ls
Usage 2 : Open new tab, with Green BG, with title "hi", in directory ~/dev, and run code .
ttab -s Grass -t hi -d '~/dev' code .
1
  • great hint. It is a pity that needs Nodejs but on the plus side it is cross platform!
    – Antonio
    Commented Oct 28, 2022 at 8:45
1

I call this script trun. I suggest putting it in a directory in your executable path. Make sure it is executable like this:

chmod +x ~/bin/trun

Then you can run commands in a new window by just adding trun before them, like this:

trun tail -f /var/log/system.log

Here's the script. It does some fancy things like pass your arguments, change the title bar, clear the screen to remove shell startup clutter, remove its file when its done. By using a unique file for each new window it can be used to create many windows at the same time.

#!/bin/bash
# make this file executable with chmod +x trun
# create a unique file in /tmp
trun_cmd=`mktemp`
# make it cd back to where we are now
echo "cd `pwd`" >$trun_cmd
# make the title bar contain the command being run
echo 'echo -n -e "\033]0;'$*'\007"' >>$trun_cmd
# clear window
echo clear >>$trun_cmd
# the shell command to execute
echo $* >>$trun_cmd
# make the command remove itself
echo rm $trun_cmd >>$trun_cmd
# make the file executable
chmod +x $trun_cmd

# open it in Terminal to run it in a new Terminal window
open -b com.apple.terminal $trun_cmd
2
  • 2
    The incorrect usage of $* throughout will wreck any nontrivial quoting in the input. You want "$@" instead.
    – tripleee
    Commented Sep 25, 2018 at 4:59
  • 1
    I was looking for open -b com.apple.terminal. Thanks
    – Ebru Yener
    Commented Jun 11, 2020 at 8:12
0

You could also invoke the new command feature of Terminal by pressing the Shift + ⌘ + N key combination. The command you put into the box will be run in a new Terminal window.

1
  • Of course useful to know, but I need to do it programatically. Having my program generate a .command file and then open it is a reasonable (if a bit hackish) solution.
    – Walt D
    Commented Jun 18, 2009 at 11:44

Not the answer you're looking for? Browse other questions tagged or ask your own question.