Откройте новую вкладку терминала из командной строки (Mac OS X)

116

Можно ли открыть новую вкладку в терминале Mac OS X из командной строки на текущей открытой вкладке?

Я знаю, что сочетание клавиш для открытия новой вкладки в Терминале - «CMD + t», но я ищу решение на основе сценария, выполняемое в командной строке.

Кальвин Ченг
источник

Ответы:

126

Попробуй это:

osascript -e 'tell application "Terminal" to activate' -e 'tell application "System Events" to tell process "Terminal" to keystroke "t" using command down'
Гордон Дэвиссон
источник
D'Oh! Я полностью пропустил ваш комментарий, нашел аналогичное решение через гугл. Одно отличие: у меня это не сработало (на 10.6.8), если только Терминал не был передним приложением, поэтому я добавил «активировать», чтобы заставить его выйти на передний план.
Гордон Дэвиссон
5
edit: Как мне поместить новую команду ex echo helloв эту новую вкладку.
ThomasReggi
22
@ThomasReggi: добавьте -e 'tell application "Terminal" to do script "echo hello" in selected tab of the front window'в конец команды osascript.
Гордон Дэвиссон
2
@clevertension для iTerm это простоopen -a iTerm ~/Applications/
onmyway133
1
@Ciastopiekarz Вы имеете в виду во вновь открытой вкладке? Используйте тот же подход, что и мой ответ ThomasReggi: добавьте -e 'tell application "Terminal" to do script "cd /path/to/target/directory" in selected tab of the front window'. Обратите внимание, что если путь исходит от переменной, вам нужно будет использовать строку в двойных кавычках вместо одинарных кавычек и экранировать внутреннюю строку в кавычках и, возможно, сам путь.
Гордон Дэвиссон
163

Обновление : этот ответ приобрел популярность благодаря функции оболочки, опубликованной ниже, которая все еще работает с OSX 10.10 (за исключением -gопции).
Однако более полнофункциональная, более надежная и протестированная версия скрипта теперь доступна в реестре npm в виде интерфейса командной строкиttab , который также поддерживает iTerm2 :

  • Если у вас установлен Node.js , просто запустите:

    npm install -g ttab
    

    (в зависимости от того, как вы установили Node.js, возможно, вам придется добавить sudo).

  • В противном случае следуйте этим инструкциям .

  • После установки запустите, ttab -hчтобы получить краткую информацию об использовании или man ttabпросмотреть руководство.


Основываясь на принятом ответе, ниже представлена удобная функция bash для открытия новой вкладки в текущем окне терминала и, при необходимости, выполнения команды (в качестве бонуса вместо этого есть вариантная функция для создания нового окна ).

Если указана команда, ее первый токен будет использоваться в качестве заголовка новой вкладки.

Примеры вызовов:

    # 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
    # Open new tab and execute script, but don't activate the new tab.
newtab -G /path/to/someScript

ПРЕДОСТЕРЕЖЕНИЕ : при запуске newtab(или newwin) из сценария исходная рабочая папка сценария будет рабочей папкой в ​​новой вкладке / окне, даже если вы измените рабочую папку внутри сценария перед вызовом newtab/ newwin- передать evalс cdкомандой в качестве обходного пути (см. пример выше).

Исходный код (вставьте, например, в свой профиль bash):

# 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|-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).
    IMPORTANT: In scripts, \`$funcName\` *statically* inherits the working folder from the
    *invoking Terminal tab* at the time of script *invocation*, even if you change the
    working folder *inside* the script before invoking \`$funcName\`.

    -g (back*g*round) causes Terminal not to activate, but within Terminal, the new tab/window
      will become the active element.
    -G causes Terminal not to activate *and* the active element within Terminal not to change;
      i.e., the previously active window and tab stay active.

    NOTE: With -g or -G specified, 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
                ;;
            -G)
                inBackground=2
                ;;
            --) # 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'

    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
        local CMD_SETTITLE="tell application \"Terminal\" to set custom title of front window to \"$tabTitle\""
            # 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.
        local quotedArgs=$(printf '%q ' "$@")
            # Step 2: Escape all backslashes again (by doubling them), because AppleScript expects that.
        local 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
            if (( inBackground )); then
                # !! Sadly, because we must create a new tab by sending a keystroke to Terminal, we must briefly activate it, then reactivate the previously active application.
                if (( inBackground == 2 )); then # Restore the previously active tab after creating the new one.
                    osascript -e "$CMD_SAVE_ACTIVE_APPNAME" -e "$CMD_SAVE_ACTIVE_TAB" -e "$CMD_ACTIVATE" -e "$CMD_NEWTAB" -e "$cmd in front window" -e "$CMD_SETTITLE" -e "$CMD_REACTIVATE_PREV_APP" -e "$CMD_REACTIVATE_PREV_TAB" >/dev/null
                else
                    osascript -e "$CMD_SAVE_ACTIVE_APPNAME" -e "$CMD_ACTIVATE" -e "$CMD_NEWTAB" -e "$cmd in front window" -e "$CMD_SETTITLE" -e "$CMD_REACTIVATE_PREV_APP" >/dev/null
                fi
            else
                osascript -e "$CMD_ACTIVATE" -e "$CMD_NEWTAB" -e "$cmd in front window" -e "$CMD_SETTITLE" >/dev/null
            fi
        else # make *window*
            # Note: $CMD_NEWWIN is not needed, as $cmd implicitly creates a new window.
            if (( inBackground )); then
                # !! Sadly, because we must create a new tab by sending a keystroke to Terminal, we must briefly activate it, then reactivate the previously active application.
                if (( inBackground == 2 )); then # Restore the previously active window after creating the new one.
                    osascript -e "$CMD_SAVE_ACTIVE_WIN" -e "$cmd" -e "$CMD_SETTITLE" -e "$CMD_REACTIVATE_PREV_WIN" >/dev/null
                else
                    osascript -e "$cmd" -e "$CMD_SETTITLE" >/dev/null
                fi
            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" -e "$cmd" -e "$CMD_SETTITLE" >/dev/null
            fi
        fi        
    else    # No command specified; simply open a new tab or window.
        if (( makeTab )); then
            if (( inBackground )); then
                # !! Sadly, because we must create a new tab by sending a keystroke to Terminal, we must briefly activate it, then reactivate the previously active application.
                if (( inBackground == 2 )); then # Restore the previously active tab after creating the new one.
                    osascript -e "$CMD_SAVE_ACTIVE_APPNAME" -e "$CMD_SAVE_ACTIVE_TAB" -e "$CMD_ACTIVATE" -e "$CMD_NEWTAB" -e "$CMD_REACTIVATE_PREV_APP" -e "$CMD_REACTIVATE_PREV_TAB" >/dev/null
                else
                    osascript -e "$CMD_SAVE_ACTIVE_APPNAME" -e "$CMD_ACTIVATE" -e "$CMD_NEWTAB" -e "$CMD_REACTIVATE_PREV_APP" >/dev/null
                fi
            else
                osascript -e "$CMD_ACTIVATE" -e "$CMD_NEWTAB" >/dev/null
            fi
        else # make *window*
            if (( inBackground )); then
                # !! Sadly, because we must create a new tab by sending a keystroke to Terminal, we must briefly activate it, then reactivate the previously active application.
                if (( inBackground == 2 )); then # Restore the previously active window after creating the new one.
                    osascript -e "$CMD_SAVE_ACTIVE_WIN" -e "$CMD_NEWWIN" -e "$CMD_REACTIVATE_PREV_WIN" >/dev/null
                else
                    osascript -e "$CMD_NEWWIN" >/dev/null
                fi
            else
                    # Note: Even though we do not strictly need to activate Terminal first, we do it so as to better visualize what is happening (the new window will appear stacked on top of an existing one).
                osascript -e "$CMD_ACTIVATE" -e "$CMD_NEWWIN" >/dev/null
            fi
        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.
}
mklement0
источник
3
@jcollum С удовольствием; рад, что вы нашли это полезным. Я только что обновил сообщение с предупреждением о рабочих папках, а также обновил код: добавлены параметры -g(не активируйте Терминал при создании новой вкладки / окна) и -G(не активируйте Терминал и не меняйте активную вкладку внутри Терминала ) - полезно, например, при запуске сервера в фоновом режиме. Обратите внимание, что при создании новой вкладки таким образом, Терминал все равно должен быть активирован на короткое время, прежде чем будет повторно активировано ранее активное приложение.
mklement0
1
@Leonardo Новая вкладка имеет тот же рабочий каталог, что и вкладка, из которой была вызвана функция. Переход в другую папку внутри скрипта перед вызовом newtab, к сожалению, НЕ работает. Обходной путь - передать evalоператор с cdкомандой newtab; например: newtab eval "cd ~/Library/Application\ Support; ls". Заключите в двойные кавычки всю переданную команду evalи используйте обратную косую черту внутри.
mklement0
1
@IntegrityFirst: По вашему предложению я переключил сигнатуры функций на function newtabи function newwin(однако, БЕЗ скобок), чтобы избежать столкновения с псевдонимами при определении функций, но обратите внимание, что при вызове псевдоним с тем же именем имеет приоритет (чтобы обойти псевдоним, ad-hoc, заключить в кавычки любую часть имени функции, например:) \newtab.
mklement0
2
@IntegrityFirst: Вот что я узнал: использование <name>() { ... }синтаксиса функции POSIX приводит <name>к расширению псевдонима , что нарушает определение функции (ошибка синтаксического анализа!), Если псевдоним <name>был определен. Обычно это не вызывает беспокойства, поскольку в обычно вызываемых скриптах раскрытие псевдонимов по умолчанию выключено. Однако в сценариях, ПОЛУЧЕННЫХ из ИНТЕРАКТИВНОЙ оболочки - например, в файлах профиля / инициализации - расширение псевдонима включено. Исправление: Используйте function <name> { ... } синтаксис, отличный от POSIX, для определения функции - <name>тогда НЕ подлежит расширению псевдонима.
mklement0
1
Спасибо! это добавляет оператор if [ "${BASH_SOURCE}" == "${0}" ]с case, чтобы его можно было вызвать как сценарий (например newtab.sh, newwin.sh): gist.github.com/westurner/01b6be85e5a51fda22a6
Уэс Тернер,
18

Вот как это делает 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
}

После добавления этого файла в ваш .bash_profile вы должны использовать эту tabкоманду, чтобы открыть текущий рабочий каталог в новой вкладке.

См. Https://github.com/revans/bash-it/blob/master/plugins/available/osx.plugin.bash#L3

dleavitt
источник
1
Очень полезно. Используя это в моем .bash_profile, я могу автоматически запускать кучу вкладок и ssh для них. Конечно, у меня включена аутентификация по паре ключей ssh
Сандип Канабар
16
osascript -e 'tell app "Terminal"
   do script "echo hello"
end tell'

Это открывает новый терминал и выполняет внутри него команду «echo hello».

Шимон Моравски
источник
3
Это сработало, но новая вкладка была создана в отдельном экземпляре Терминала. Остается ли новая вкладка в текущем экземпляре моего Терминала?
Calvin Cheng,
Кстати, вы можете использовать do script ""с пустой строкой для создания нового терминала без ввода команды.
Крис Пейдж
9

Если вы используете oh-my-zsh (который должен использовать каждый модный компьютерщик), после активации плагина «osx» .zshrcпросто введите tabкоманду; он откроет новую вкладку и cdв каталоге, в котором вы были.

CharlesB
источник
Смотрится очень интересно. В чем разница между zcsh и обычным bash?
Calvin Cheng,
Они очень похожи, но, что самое интересное, у них есть интеллектуальное, мощное завершение табуляции и автокоррекция. Смотрите хорошее сравнение здесь . Oh-my-zsh устанавливает среду с красивыми и удобными настройками / плагинами, чтобы вы могли начать работу
CharlesB
Взглянул на ссылку сравнения CharlesB. Очень интересно. Похоже на оболочку BPython по сравнению с оболочкой iPython.
Calvin Cheng
zsh удается сохранить еще больше старого мусора, чтобы не потерять контроль
Джеймс
Вы можете предоставить дополнительную информацию по этому поводу? Что такое команда табуляции? Вход, tabкажется, ничего не делает
Солвитиг
7

Сочетание клавиш cmd-tоткрывает новую вкладку, поэтому вы можете передать это нажатие клавиши команде OSA следующим образом:

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

Азиз Альто
источник
6

Я добавил их в свой .bash_profile, чтобы иметь доступ к tabname и 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
}

Поэтому, когда вы находитесь на определенной вкладке, вы можете просто ввести

tabname "New TabName"

чтобы организовать все открытые вкладки, которые у вас есть. Это намного лучше, чем получить информацию на вкладке и изменить ее там.

richtera
источник
Спасибо. вы знаете, как сохранить имя вкладки после того, как я сделаю ssh из вкладки и выйду из сеанса ssh?
anjanb 05
4

Я знаю, что это старый пост, но у меня это сработало:

open -a Terminal "`pwd`"

Чтобы запустить команду, как указано ниже, потребуется немного уловки:

echo /sbin/ping 8.8.8.8 > /tmp/tmp.sh;chmod a+x /tmp/tmp.sh;open -a Terminal /tmp/tmp.sh
neophytte
источник
Очень хорошо! Как мне это сделать, если я хочу передать команды, которые будут выполняться в новом экземпляре Терминала? : D
Strazan
@Strazan отредактировал ответ выше ... получайте удовольствие !! Похоже, терминал примет такой параметр ...
neophytte
3

когда вы находитесь в окне терминала, команда + n => открывает новый терминал, а команда + t => открывает новую вкладку в текущем окне терминала.

xdev
источник
1
это должно работать из командной строки. в основном скрипт. потому что это повторяющаяся задача
Джанфранко П.
2

Если вы используете iTerm, эта команда откроет новую вкладку:

osascript -e 'tell application "iTerm" to activate' -e 'tell application "System Events" to tell process "iTerm" to keystroke "t" using command down'
Эндрю Шрайбер
источник
Если вам нужно добавить это в .zshrc или .bashrc, вы можете сделать это с помощью функции вместо псевдонима (из-за всех экранирований вам в конечном итоге придется это сделать). stackoverflow.com/a/20111135/1401336
Vigrant
@ Эндрю Шрайбер: Но управление не переносится на новую вкладку. Я имею в виду, что если у вас есть код после открытия новой вкладки, этот код выполняется на исходной вкладке. Есть ли способ указать скрипту обрабатывать следующие команды на новой вкладке?
Ashwin
1
open -n -a Terminal

и вы можете передать целевой каталог в качестве параметра

open -n -a Terminal /Users
Эвертон Сантос
источник
Это открывает для меня новое окно. Не вкладка.
stack-delay
0

А как насчет этого простого фрагмента, основанного на стандартной команде сценария (echo):

# set mac osx's terminal title to "My Title"
echo -n -e "\033]0;My Title\007"
Адриен Джоли
источник
0

С установленным X (например, из homebrew или Quartz) простой «xterm &» делает (почти) трюк, он открывает новое окно терминала (хотя и не вкладку).

Иммануил Кант
источник