160

Is it possible to open a new tab in Mac OS X's terminal from the command line in a currently opened tab?

I know that the keyboard shortcut to open a new tab in Terminal is "CMD+t" but I am looking for a script-based solution executed in the command line.

16 Answers 16

182

Update:

  • This answer gained popularity based on the shell function posted below, which still works as of macOS Sonoma (14.x).
    However, a more fully featured, more robust, tested script version (stand-alone CLI) is now available at the npm registry as CLI ttab, which also supports iTerm2 and, on Linux distros, gnome-terminal:

    • On macOS, if you have Homebrew installed, run:

      brew tap mklement0/ttab https://github.com/mklement0/ttab.git
      brew install mklement0/ttab/ttab
      
    • Alternatively and on Linux, if you have Node.js installed, simply run (depending on how you installed Node.js, you may have to prepend sudo):

      npm install -g ttab
      
    • Otherwise, follow these instructions to download the script directly from the GitHub repository.

    • Once installed, run ttab -h for concise usage information, or man ttab to view the manual.


Building on the accepted answer, below is a bash convenience function, newtab, for opening a new tab in the current Terminal window and optionally executing a command (as a bonus, there's a variant function for creating a new window instead, newwin).

If a command is specified, its first token is used as the new tab's title.

Sample invocations:

    # Get command-line help.
newtab -h
    # Simpy open new tab.
newtab
    # Open new tab and execute command (quoted parameters are supported).
newtab ls -l "$Home/Library/Application Support"
    # Open a new tab with a given working directory and execute a command;
    # Double-quote the command passed to `eval` and use backslash-escaping inside.
newtab eval "cd ~/Library/Application\ Support; ls"
    # Open new tab, execute commands, close tab.
newtab eval "ls \$HOME/Library/Application\ Support; echo Press a key to exit.; read -s -n 1; exit"
    # Open new tab and execute script.
newtab /path/to/someScript
    # Open new tab, execute script, close tab.
newtab exec /path/to/someScript

newtab and newwin source code (paste into your bash profile, for instance):

# Opens a new tab in the current Terminal window and optionally executes a command.
# When invoked via a function named 'newwin', opens a new Terminal *window* instead.
function newtab {

    # If this function was invoked directly by a function named 'newwin', we open a new *window* instead
    # of a new tab in the existing window.
    local funcName=$FUNCNAME
    local targetType='tab'
    local targetDesc='new tab in the active Terminal window'
    local makeTab=1
    case "${FUNCNAME[1]}" in
        newwin)
            makeTab=0
            funcName=${FUNCNAME[1]}
            targetType='window'
            targetDesc='new Terminal window'
            ;;
    esac

    # Command-line help.
    if [[ "$1" == '--help' || "$1" == '-h' ]]; then
        cat <<EOF
Synopsis:
    $funcName [-g] [command [param1 ...]]

Description:
    Opens a $targetDesc and optionally executes a command.

    The new $targetType will run a login shell (i.e., load the user's shell profile) and inherit
    the working folder from this shell (the active Terminal tab).

    -g (back*g*round) causes Terminal not to activate, but within Terminal, the new tab/window
      will become the active element.

    NOTE: With -g, for technical reasons, Terminal will still activate *briefly* when
    you create a new tab (creating a new window is not affected).

    When a command is specified, its first token will become the new ${targetType}'s title.
    Quoted parameters are handled properly.

    To specify multiple commands, use 'eval' followed by a single, *double*-quoted string
    in which the commands are separated by ';' Do NOT use backslash-escaped double quotes inside
    this string; rather, use backslash-escaping as needed.
    Use 'exit' as the last command to automatically close the tab when the command
    terminates; precede it with 'read -s -n 1' to wait for a keystroke first.

    Alternatively, pass a script name or path; prefix with 'exec' to automatically
    close the $targetType when the script terminates.

Examples:
    $funcName ls -l "\$Home/Library/Application Support"
    $funcName eval "ls \\\$HOME/Library/Application\ Support; echo Press a key to exit.; read -s -n 1; exit"
    $funcName /path/to/someScript
    $funcName exec /path/to/someScript
EOF
        return 0
    fi

    # Option-parameters loop.
    inBackground=0
    while (( $# )); do
        case "$1" in
            -g)
                inBackground=1
                ;;
            --) # Explicit end-of-options marker.
                shift   # Move to next param and proceed with data-parameter analysis below.
                break
                ;;
            -*) # An unrecognized switch.
                echo "$FUNCNAME: PARAMETER ERROR: Unrecognized option: '$1'. To force interpretation as non-option, precede with '--'. Use -h or --h for help." 1>&2 && return 2
                ;;
            *)  # 1st argument reached; proceed with argument-parameter analysis below.
                break
                ;;
        esac
        shift
    done

    # All remaining parameters, if any, make up the command to execute in the new tab/window.

    local CMD_PREFIX='tell application "Terminal" to do script'

        # Command for opening a new Terminal window (with a single, new tab).
    local CMD_NEWWIN=$CMD_PREFIX    # Curiously, simply executing 'do script' with no further arguments opens a new *window*.
        # Commands for opening a new tab in the current Terminal window.
        # Sadly, there is no direct way to open a new tab in an existing window, so we must activate Terminal first, then send a keyboard shortcut.
    local CMD_ACTIVATE='tell application "Terminal" to activate'
    local CMD_NEWTAB='tell application "System Events" to keystroke "t" using {command down}'
        # For use with -g: commands for saving and restoring the previous application
    local CMD_SAVE_ACTIVE_APPNAME='tell application "System Events" to set prevAppName to displayed name of first process whose frontmost is true'
    local CMD_REACTIVATE_PREV_APP='activate application prevAppName'
        # For use with -G: commands for saving and restoring the previous state within Terminal
    local CMD_SAVE_ACTIVE_WIN='tell application "Terminal" to set prevWin to front window'
    local CMD_REACTIVATE_PREV_WIN='set frontmost of prevWin to true'
    local CMD_SAVE_ACTIVE_TAB='tell application "Terminal" to set prevTab to (selected tab of front window)'
    local CMD_REACTIVATE_PREV_TAB='tell application "Terminal" to set selected of prevTab to true'
    local CMD_SETTITLE
    local quotedArgs
    local cmd
    local cmdArgsArray=()
    local setTitleArgsArray=()

    if (( $# )); then # Command specified; open a new tab or window, then execute command.
            # Use the command's first token as the tab title.
        local tabTitle=$1
        case "$tabTitle" in
            exec|eval) # Use following token instead, if the 1st one is 'eval' or 'exec'.
                tabTitle=$(echo "$2" | awk '{ print $1 }') 
                ;;
            cd) # Use last path component of following token instead, if the 1st one is 'cd'
                tabTitle=$(basename "$2")
                ;;
        esac
        CMD_SETTITLE="tell application \"Terminal\" to set custom title of front window to \"$tabTitle\""
        setTitleArgsArray=('-e' "$CMD_SETTITLE")
    fi
    # Formulate an escaped form of the command to execute in the new tab.
    # Note: A command is *implicitly* used in *script* use, namely a `cd` command
    #       to ensure that the new tab inherits the script's working dir. reliably.
        # The tricky part is to quote the command tokens properly when passing them to AppleScript:
        # Step 1: Quote all parameters (as needed) using printf '%q' - this will perform backslash-escaping.
    # Start with a `cd` command in *script* (non-interactive) use.
    [[ -z $PS1 ]] && quotedArgs="cd -- $(printf '%q' "$PWD")"
    [[ -n $quotedArgs && $# -gt 0 ]] && quotedArgs+=' && '
    # Append the user-specific command, if any.
    (( $# )) && quotedArgs+=$(printf '%q ' "$@")
        # Step 2: Escape all backslashes again (by doubling them), because AppleScript expects that.
    cmd="$CMD_PREFIX \"${quotedArgs//\\/\\\\}\""
    # cmd="$CMD_PREFIX \"${quotedArgs}\""
        # Open new tab or window, execute command, and assign tab title.
        # '>/dev/null' suppresses AppleScript's output when it creates a new tab.
    if (( makeTab )); then
        [[ -n $quotedArgs ]] && cmdArgsArray=('-e' "$cmd in front window")
        if (( inBackground )); then
            osascript -e "$CMD_SAVE_ACTIVE_APPNAME" -e "$CMD_ACTIVATE" -e "$CMD_NEWTAB" "${cmdArgsArray[@]}" "${setTitleArgsArray[@]}" -e "$CMD_REACTIVATE_PREV_APP" >/dev/null
        else
            osascript -e "$CMD_ACTIVATE" -e "$CMD_NEWTAB" "${cmdArgsArray[@]}" "${setTitleArgsArray[@]}" >/dev/null
        fi
    else # make *window*

        # Note: $CMD_NEWWIN by itself implicitly creates a new window.
        [[ -n $quotedArgs ]] && cmdArgsArray=('-e' "$cmd") || cmdArgsArray=('-e' "$CMD_NEWWIN")
        if (( inBackground )); then
            osascript "${cmdArgsArray[@]}" "${setTitleArgsArray[@]}" >/dev/null
        else
                # Note: Even though we do not strictly need to activate Terminal first, we do it, as assigning the custom title to the 'front window' would otherwise sometimes target the wrong window.
            osascript -e "$CMD_ACTIVATE" "${cmdArgsArray[@]}" "${setTitleArgsArray[@]}" >/dev/null
        fi
    fi        

}

# Opens a new Terminal window and optionally executes a command.
function newwin {
    newtab "$@" # Simply pass through to 'newtab', which will examine the call stack to see how it was invoked.
}
Sign up to request clarification or add additional context in comments.

13 Comments

@jcollum My pleasure; glad you find it useful. I just updated the post with a caveat re working folders and also updated the code: added options -g (don't activate Terminal when creating the new tab/window) and -G (don't activate Terminal and don't change the active tab inside Terminal) - helpful, for instance, when starting a server in the background. Note that when creating a new tab this way, Terminal still has to be activated briefly before the previously active application is reactivated.
@Leonardo The new tab has the same working directory as the tab from which the function was invoked. Changing to a different folder inside a script before calling newtab, unfortunately, does NOT work. The workaround is to pass an eval statement with a cd command to newtab; e.g.: newtab eval "cd ~/Library/Application\ Support; ls". Double-quote the entire command passed to eval, and use backslash-escaping inside.
@IntegrityFirst: Per your suggestion I've switched the functions signatures to function newtab and function newwin (however, NO parentheses), so that should avoid the collision with aliases when defining the functions, but note that on invocation an alias of the same name takes precedence (to bypass the alias, ad-hoc, quote any part of the function name, e.g.: \newtab).
@IntegrityFirst: Here's what I learned: Using POSIX <name>() { ... } function syntax makes <name> subject to alias expansion, which breaks the function definition (parsing error!) if an alias <name> happens to be defined. Not typically a concern, since in normally invoked scripts alias expansion is turned OFF by default. However, in scripts SOURCED from an INTERACTIVE shell - such as in profile / initialization files - alias expansion IS turned on. Fix: Use non-POSIX function <name> { ... } syntax to define the function - <name> is then NOT subject to alias expansion.
Thanks! this adds an if [ "${BASH_SOURCE}" == "${0}" ] with a case statement so it can be called as a script (e.g. newtab.sh, newwin.sh): gist.github.com/westurner/01b6be85e5a51fda22a6
|
150

Try this:

osascript -e 'tell application "Terminal" to activate' \
  -e 'tell application "System Events" to tell process "Terminal" to keystroke "t" using command down' \
  -e 'tell application "Terminal" to do script "echo hello" in selected tab of the front window'

But if you need to run dynamic command, set myCommand variable, and replace last line with:

-e "tell application \"Terminal\" to do script \"${myCommand};\" in selected tab of the front window";

9 Comments

D'Oh! I missed your comment completely, found a similar solution via google. One difference: it didn't work for me (on 10.6.8) unless Terminal was the frontmost application, so I added the "activate" to force it to the front.
edit: How do I put a new command e.x echo hello into this new tab.
@ThomasReggi: Add -e 'tell application "Terminal" to do script "echo hello" in selected tab of the front window' to the end of the osascript command.
@clevertension for iTerm it is just open -a iTerm ~/Applications/
@Ciastopiekarz Do you mean in the newly opened tab? Use the same approach as my answer to ThomasReggi: add -e 'tell application "Terminal" to do script "cd /path/to/target/directory" in selected tab of the front window'. Note that if the path is coming from a variable, you'll need to use a double-quoted string instead of single-quoted, and escape the inner quoted string, and probably the path itself.
|
25
osascript -e 'tell app "Terminal"
   do script "echo hello"
end tell'

This opens a new terminal and executes the command "echo hello" inside it.

2 Comments

This worked but the new tab was created in a separate instance of Terminal. Is there anyway the new tab remains in the current instance of my Terminal?
By the way, you can use do script "" with an empty string to create a new terminal without issuing a command.
20

Here's how it's done by bash_it:

function tab() {
  osascript 2>/dev/null <<EOF
    tell application "System Events"
      tell process "Terminal" to keystroke "t" using command down
    end
    tell application "Terminal"
      activate
      do script with command "cd \"$PWD\"; $*" in window 1
    end tell
EOF
}

After adding this to your .bash_profile, you'd use the tab command to open the current working directory in a new tab.

See: https://github.com/revans/bash-it/blob/master/plugins/available/osx.plugin.bash#L3

1 Comment

Very helpful. Using this in my .bash_profile, I'm able to launch a bunch of tabs and ssh to them automatically. Of course, I've ssh key-pair authentication enabled
19

If you use oh-my-zsh (which every trendy geek should use), after activating the macos plugin in .zshrc, simply enter the tab command; it will open a new tab and cd in the directory your were on.

9 Comments

It looks very interesting. What's the difference between zcsh and conventional bash?
Can you provide more information on this? What is the tab command? Entering tab doesn't appear to do anything
@Solvitieg it’s 9 years old it might be obsolete now
@Solvitieg @CharlesB Not obsolete. Just enabled and used it. Be sure to enable the osx plugin in your .zshrc file.
|
8

I added these to my .bash_profile so I can have access to tabname and newtab

tabname() {
  printf "\e]1;$1\a"
}

new_tab() {
  TAB_NAME=$1
  COMMAND=$2
  osascript \
    -e "tell application \"Terminal\"" \
    -e "tell application \"System Events\" to keystroke \"t\" using {command down}" \
    -e "do script \"printf '\\\e]1;$TAB_NAME\\\a'; $COMMAND\" in front window" \
    -e "end tell" > /dev/null
}

So when you're on a particular tab you can just type

tabname "New TabName"

to organize all the open tabs you have. It's much better than getting info on the tab and changing it there.

1 Comment

thanks. do you know how to retain the tab name after I do a ssh from the tab and exit from the ssh session ?
8

I know this is an old post, but this worked for me:

open -a Terminal "`pwd`"

To run a command as requested below takes some jiggery:

echo /sbin/ping 8.8.8.8 > /tmp/tmp.sh;chmod a+x /tmp/tmp.sh;open -a Terminal /tmp/tmp.sh

5 Comments

Very nice! How do I do if I wanna pass commands that will run in the new instance of Terminal? :D
@Strazan edited answer above ... have fun!! Looks like terminal will take a parameter like that ...
When I tried this, it opened a new terminal window rather than open a new tab on an existing one
What OS & Terminal are you running? Worked for me today on Darwin Kernel Version 20.3.0 & Terminal Version 2.11 (440)
running this from a script didn't let the terminal be controlled by user (was expecting stdin or whatever)
6

The keyboard shortcut cmd-t opens a new tab, so you can pass this keystroke to OSA command as follows:

osascript -e 'tell application "System Events"' -e 'keystroke "t" using command down' -e 'end tell'

Comments

3
open -n -a Terminal

and you can pass the target directory as parameter

open -n -a Terminal /Users

1 Comment

This opens a new window for me. Not a tab.
2

when you are in a terminal window, command + n => opens a new terminal and command + t => opens a new tab in current terminal window

1 Comment

this has to work from the commandline. basically a script. because it's a repetitive task
1

If you are using iTerm this command will open a new tab:

osascript -e 'tell application "iTerm" to activate' -e 'tell application "System Events" to tell process "iTerm" to keystroke "t" using command down'

2 Comments

If you need to add this to a .zshrc or a .bashrc you can do so with a function instead of an alias (because of all the escaping you will end up having to do). stackoverflow.com/a/20111135/1401336
@Andrew Schreiber : But the control does not transfer to the new tab. I mean if you have some code after opening the new tab, that code gets executed in the original tab. Is there a way to tell the script to process the following commands in the new tab?
0

What about this simple snippet, based on a standard script command (echo):

# set mac osx's terminal title to "My Title"
echo -n -e "\033]0;My Title\007"

Comments

0

With X installed (e.g. from homebrew, or Quartz), a simple "xterm &" does (nearly) the trick, it opens a new terminal window (not a tab, though).

Comments

0

I made a simplified version that works around the AppleScript bug that generates a new UI.

on run argv
    set scpt to first item in argv
    set flag to application "Terminal" is not running
    tell application "Terminal"
        do script scpt
        activate
        delay 1.0E-5
        if flag then close back window
    end tell
end run

Or this also works

open -a Terminal.app path/file.sh

Comments

0

Another option is to use make to organize your terminal tab launching a little better. For example, you could create a make file that looks like this:

Makefile

.PHONY:launchtabgroup1
launchtabgroup1:
    chmod u+r+x scripts/launch_tabgroup1.sh
    scripts/launch_tabgroup1.sh

.PHONY:launchtabgroup2
launchtabgroup2:
    chmod u+r+x scripts/launch_tabgroup2.sh
    scripts/launch_tabgroup2.sh

Then within a subdirectory of where you store your make file, create a scripts directory that has all the commands to open whatever tabs you want for that group. Like this:

launch_tabgroup1.sh

#!/usr/bin/env sh


osascript -e 'tell application "Terminal" to activate' \
  -e 'tell application "System Events" to tell process "Terminal" to keystroke "t" using command down' \
  -e 'tell application "Terminal" to do script "cd ../path/to/desired/directory" in selected tab of the front window'

osascript -e 'tell application "Terminal" to activate' \
  -e 'tell application "System Events" to tell process "Terminal" to keystroke "t" using command down' \
  -e 'tell application "Terminal" to do script "cd ../path/to/desired/directory" in selected tab of the front window'

osascript -e 'tell application "Terminal" to activate' \
  -e 'tell application "System Events" to tell process "Terminal" to keystroke "t" using command down' \
  -e 'tell application "Terminal" to do script "cd ../path/to/desired/directory" in selected tab of the front window'

You would make corresponding entries in the Makefile that call the respective .sh files, which spin up the tabs in the group!

To actually spin up the tab groups navigate to the Makefile directory and run make launchtabgroup1 or make launchtabgroup2.

Comments

0

to just open multiple directories:

open -a Terminal dorian-yaml-*

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.