Abra a nova aba Terminal na linha de comando (Mac OS X)

116

É possível abrir uma nova guia no terminal do Mac OS X a partir da linha de comando em uma guia aberta atualmente?

Eu sei que o atalho de teclado para abrir uma nova guia no Terminal é "CMD + t", mas estou procurando uma solução baseada em script executada na linha de comando.

Calvin Cheng
fonte

Respostas:

126

Experimente isto:

osascript -e 'tell application "Terminal" to activate' -e 'tell application "System Events" to tell process "Terminal" to keystroke "t" using command down'
Gordon Davisson
fonte
D'Oh! Perdi completamente o seu comentário, encontrei uma solução semelhante via google. Uma diferença: não funcionou para mim (no 10.6.8) a menos que o Terminal fosse o aplicativo mais avançado, então adicionei o "activate" para forçá-lo para frente.
Gordon Davisson
5
editar: Como faço para colocar um novo comando ex echo hellonesta nova guia.
ThomasReggi de
22
@ThomasReggi: Adicionar -e 'tell application "Terminal" to do script "echo hello" in selected tab of the front window'ao final do comando osascript.
Gordon Davisson
2
@clevertension para iTerm é apenasopen -a iTerm ~/Applications/
onmyway133
1
@Ciastopiekarz Você quer dizer na guia recém-aberta? Use a mesma abordagem que minha resposta a ThomasReggi: add -e 'tell application "Terminal" to do script "cd /path/to/target/directory" in selected tab of the front window'. Observe que se o caminho vier de uma variável, você precisará usar uma string entre aspas duplas em vez de aspas simples e escapar da string entre aspas internas e provavelmente do próprio caminho.
Gordon Davisson
163

Atualização : Esta resposta ganhou popularidade com base na função shell postada abaixo, que ainda funciona a partir do OSX 10.10 (com exceção da -gopção).
No entanto, uma versão de script testada com mais recursos e mais robusta agora está disponível no registro npm como CLIttab , que também oferece suporte a iTerm2 :

  • Se você tiver o Node.js instalado, basta executar:

    npm install -g ttab
    

    (dependendo de como você instalou o Node.js, pode ser necessário preceder sudo).

  • Caso contrário, siga estas instruções .

  • Depois de instalado, execute ttab -hpara obter informações de uso concisas ou man ttabpara ver o manual.


Com base na resposta aceita, a seguir está uma função de conveniência bash para abrir uma nova guia na janela atual do Terminal e, opcionalmente, executar um comando (como um bônus, há uma função variante para criar uma nova janela ).

Se um comando for especificado, seu primeiro token será usado como o título da nova guia.

Exemplos de invocações:

    # 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

CAVEAT : Quando você executa newtab(ou newwin) a partir de um script, a pasta de trabalho inicial do script será a pasta de trabalho na nova guia / janela, mesmo se você alterar a pasta de trabalho dentro do script antes de invocar newtab/ newwin- passar evalcom um cdcomando como uma solução alternativa (veja o exemplo acima).

Código-fonte (cole em seu perfil bash, por exemplo):

# 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
fonte
3
@jcollum Com prazer; feliz por você achar isso útil. Acabei de atualizar a postagem com uma advertência sobre as pastas de trabalho e também atualizei o código: opções adicionadas -g(não ative o Terminal ao criar a nova guia / janela) e -G(não ative o Terminal e não altere a guia ativa dentro do Terminal ) - útil, por exemplo, ao iniciar um servidor em segundo plano. Observe que, ao criar uma nova guia dessa forma, o Terminal ainda precisa ser ativado brevemente antes que o aplicativo anteriormente ativo seja reativado.
mklement0
1
@Leonardo A nova guia possui o mesmo diretório de trabalho da guia a partir da qual a função foi chamada. Mudar para uma pasta diferente dentro de um script antes de chamar newtab, infelizmente, NÃO funciona. A solução alternativa é passar uma evalinstrução com um cdcomando para newtab; por exemplo: newtab eval "cd ~/Library/Application\ Support; ls". Coloque aspas em todo o comando passado evale use o escape de barra invertida dentro dele.
mklement0
1
@IntegrityFirst: Por sua sugestão, mudei as assinaturas das funções para function newtabe function newwin(no entanto, SEM parênteses), de modo que deve evitar a colisão com apelidos ao definir as funções, mas observe que na invocação um apelido com o mesmo nome tem precedência (para ignore o alias, ad-hoc, coloque qualquer parte do nome da função, por exemplo \newtab:).
mklement0
2
@IntegrityFirst: Aqui está o que aprendi: Usar a <name>() { ... }sintaxe da função POSIX <name>sujeita a expansão do alias , que quebra a definição da função (erro de análise!) Se um alias <name>for definido. Normalmente não é uma preocupação, uma vez que em scripts normalmente invocados, a expansão do alias está DESLIGADA por padrão. No entanto, em scripts ORIGINADOS de um shell INTERATIVO - como em arquivos de perfil / inicialização - a expansão de alias ESTÁ ativada. Correção: Use function <name> { ... } sintaxe não POSIX para definir a função - <name>então NÃO está sujeito à expansão de alias.
mklement0
1
Obrigado! isso adiciona um if [ "${BASH_SOURCE}" == "${0}" ]com uma instrução de caso para que possa ser chamado como um script (por exemplo newtab.sh, newwin.sh): gist.github.com/westurner/01b6be85e5a51fda22a6
Wes Turner
18

Veja como isso é feito por 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
}

Depois de adicionar isso ao seu .bash_profile, você usaria o tabcomando para abrir o diretório de trabalho atual em uma nova guia.

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

dleavitt
fonte
1
Muito útil. Usando isso em meu .bash_profile, sou capaz de lançar um monte de guias e ssh para eles automaticamente. Claro, eu habilitei a autenticação de par de chaves SSH
Sandeep Kanabar
16
osascript -e 'tell app "Terminal"
   do script "echo hello"
end tell'

Isso abre um novo terminal e executa o comando "echo hello" dentro dele.

Szymon Morawski
fonte
3
Isso funcionou, mas a nova guia foi criada em uma instância separada do Terminal. A nova guia permanece na instância atual do meu Terminal?
Calvin Cheng
A propósito, você pode usar do script ""com uma string vazia para criar um novo terminal sem emitir um comando.
Chris Página
9

Se você usar oh-my-zsh (que todo geek da moda deve usar), depois de ativar o plugin "osx" .zshrc, simplesmente digite o tabcomando; ele abrirá uma nova guia e cdno diretório em que você estava.

CharlesB
fonte
Parece muito interessante. Qual é a diferença entre zcsh e bash convencional?
Calvin Cheng
Eles são muito semelhantes, mas o mais interessante é que possuem um preenchimento de guias poderoso e inteligente e correção automática. Veja uma boa comparação aqui . Oh-my-zsh está configurando um ambiente com configurações / plug-ins agradáveis ​​e úteis para você começar
CharlesB
Deu uma olhada rápida no link de comparação de CharlesB. Muito interessante. Soa quase como shell BPython versus shell iPython.
Calvin Cheng
zsh consegue manter ainda mais coisas velhas para perder o controle
Tiago,
Você pode fornecer mais informações sobre isso? Qual é o comando tab? Entrar tabnão parece fazer nada
Solvitieg
7

O atalho do teclado cmd-tabre uma nova guia, então você pode passar esta combinação de teclas para o comando OSA da seguinte maneira:

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

Aziz Alto
fonte
6

Eu adicionei isso ao meu .bash_profile para ter acesso a tabname e 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
}

Então, quando você estiver em uma guia específica, basta digitar

tabname "New TabName"

para organizar todas as guias abertas que você possui. É muito melhor do que obter informações na guia e alterá-las lá.

Richtera
fonte
obrigado. você sabe como manter o nome da guia após eu fazer um ssh da guia e sair da sessão de ssh?
anjanb
4

Eu sei que esta é uma postagem antiga, mas funcionou para mim:

open -a Terminal "`pwd`"

Para executar um comando conforme solicitado a seguir, é necessário alguns truques:

echo /sbin/ping 8.8.8.8 > /tmp/tmp.sh;chmod a+x /tmp/tmp.sh;open -a Terminal /tmp/tmp.sh
neófito
fonte
Muito agradável! Como faço para passar comandos que serão executados na nova instância do Terminal? : D
Strazan
@Strazan editou a resposta acima ... divirta-se !! Parece que o terminal terá um parâmetro como esse ...
neófito
3

quando você está em uma janela de terminal, command + n => abre um novo terminal e command + t => abre uma nova guia na janela do terminal atual

xdev
fonte
1
isso tem que funcionar a partir da linha de comando. basicamente um script. porque é uma tarefa repetitiva
Gianfranco P.
2

Se você estiver usando o iTerm, este comando abrirá uma nova guia:

osascript -e 'tell application "iTerm" to activate' -e 'tell application "System Events" to tell process "iTerm" to keystroke "t" using command down'
Andrew Schreiber
fonte
Se precisar adicionar isso a um .zshrc ou .bashrc, você pode fazer isso com uma função em vez de um alias (por causa de todos os escapes que você acabará tendo que fazer). stackoverflow.com/a/20111135/1401336
Vigrant
@Andrew Schreiber: Mas o controle não transfere para a nova aba. Quero dizer, se você tiver algum código depois de abrir a nova guia, esse código será executado na guia original. Existe uma maneira de dizer ao script para processar os seguintes comandos na nova guia?
Ashwin de
1
open -n -a Terminal

e você pode passar o diretório de destino como parâmetro

open -n -a Terminal /Users
Everton Santos
fonte
Isso abre uma nova janela para mim. Não é uma guia.
stack-delay
0

E este snippet simples, baseado em um comando de script padrão (echo):

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

Com o X instalado (por exemplo, do homebrew ou Quartz), um simples "xterm &" faz (quase) o truque, ele abre uma nova janela de terminal (mas não uma guia).

Immanuel Kant
fonte