Abra una nueva pestaña Terminal desde la línea de comando (Mac OS X)

116

¿Es posible abrir una nueva pestaña en el terminal de Mac OS X desde la línea de comando en una pestaña abierta actualmente?

Sé que el atajo de teclado para abrir una nueva pestaña en Terminal es "CMD + t" pero estoy buscando una solución basada en script ejecutada en la línea de comando.

Calvin Cheng
fuente

Respuestas:

126

Prueba esto:

osascript -e 'tell application "Terminal" to activate' -e 'tell application "System Events" to tell process "Terminal" to keystroke "t" using command down'
Gordon Davisson
fuente
¡Oh! Me perdí completamente tu comentario, encontré una solución similar a través de Google. Una diferencia: no funcionó para mí (en 10.6.8) a menos que Terminal fuera la aplicación más avanzada, así que agregué "activar" para forzarla al frente.
Gordon Davisson
5
editar: ¿Cómo pongo un nuevo comando ex echo helloen esta nueva pestaña?
ThomasReggi
22
@ThomasReggi: Agregar -e 'tell application "Terminal" to do script "echo hello" in selected tab of the front window'al final del comando osascript.
Gordon Davisson
2
@clevertension para iTerm es soloopen -a iTerm ~/Applications/
onmyway133
1
@Ciastopiekarz ¿Te refieres a la pestaña recién abierta? Use el mismo enfoque que mi respuesta a ThomasReggi: agregar -e 'tell application "Terminal" to do script "cd /path/to/target/directory" in selected tab of the front window'. Tenga en cuenta que si la ruta proviene de una variable, deberá usar una cadena entre comillas dobles en lugar de entre comillas simples y escapar de la cadena entre comillas internas, y probablemente la ruta en sí.
Gordon Davisson
163

Actualización : esta respuesta ganó popularidad según la función de shell publicada a continuación, que aún funciona a partir de OSX 10.10 (con la excepción de la -gopción).
Sin embargo, ahora está disponible una versión de script más completa, más robusta y probada en el registro de npm como CLIttab , que también es compatible con iTerm2 :

  • Si tiene Node.js instalado, simplemente ejecute:

    npm install -g ttab
    

    (dependiendo de cómo haya instalado Node.js, es posible que deba anteponer sudo).

  • De lo contrario, siga estas instrucciones .

  • Una vez instalado, ejecute ttab -h para obtener información de uso concisa o man ttabpara ver el manual.


Sobre la base de la respuesta aceptada, a continuación se muestra una función de conveniencia de bash para abrir una nueva pestaña en la ventana de Terminal actual y, opcionalmente, ejecutar un comando (como beneficio adicional, hay una función variante para crear una nueva ventana ).

Si se especifica un comando, su primer token se utilizará como título de la nueva pestaña.

Ejemplos de invocaciones:

    # 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

AVISO : Cuando ejecuta newtab(o newwin) desde un script, la carpeta de trabajo inicial del script será la carpeta de trabajo en la nueva pestaña / ventana, incluso si cambia la carpeta de trabajo dentro del script antes de invocar newtab/newwin - pass evalcon un cdcomando como solución alternativa (ver ejemplo arriba).

Código fuente (péguelo en su perfil de bash, por ejemplo):

# 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
fuente
3
@jcollum Un placer; Me alegra que lo encuentres útil. Acabo de actualizar la publicación con una advertencia sobre las carpetas de trabajo y también actualicé el código: opciones agregadas -g(no activar Terminal al crear la nueva pestaña / ventana) y -G(no activar Terminal y no cambiar la pestaña activa dentro de Terminal ) - útil, por ejemplo, al iniciar un servidor en segundo plano. Tenga en cuenta que al crear una nueva pestaña de esta manera, Terminal aún debe activarse brevemente antes de que se reactive la aplicación previamente activa.
mklement0
1
@Leonardo La nueva pestaña tiene el mismo directorio de trabajo que la pestaña desde la que se invocó la función. Cambiar a una carpeta diferente dentro de un script antes de llamar newtab, desafortunadamente, NO funciona. La solución es pasar una evaldeclaración con un cdcomando a newtab; por ejemplo: newtab eval "cd ~/Library/Application\ Support; ls". Entre comillas todo el comando pasado evaly use la barra invertida-escape adentro.
mklement0
1
@IntegrityFirst: según su sugerencia, cambié las firmas de las funciones a function newtaby function newwin(sin embargo, SIN paréntesis), por lo que debería evitar la colisión con los alias al definir las funciones, pero tenga en cuenta que en la invocación, un alias del mismo nombre tiene prioridad (para omita el alias, ad-hoc, cite cualquier parte del nombre de la función, por ejemplo:) \newtab.
mklement0
2
@IntegrityFirst: Esto es lo que aprendí: el uso de la <name>() { ... }sintaxis de la función POSIX está <name>sujeta a la expansión de alias , lo que rompe la definición de la función (¡error de análisis!) Si <name>se define un alias . No suele ser una preocupación, ya que en los scripts que se invocan normalmente, la expansión de alias está desactivada de forma predeterminada. Sin embargo, en scripts FUENTES de un shell INTERACTIVO, como en archivos de perfil / inicialización, la expansión de alias ESTÁ activada. Solución: use una function <name> { ... } sintaxis que no sea POSIX para definir la función; <name>entonces NO está sujeta a expansión de alias.
mklement0
1
¡Gracias! esto agrega una if [ "${BASH_SOURCE}" == "${0}" ]declaración con un caso para que se pueda llamar como un script (por ejemplo newtab.sh, newwin.sh): gist.github.com/westurner/01b6be85e5a51fda22a6
Wes Turner
18

Así es como bash_it lo hace :

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
}

Después de agregar esto a su .bash_profile, usaría el tabcomando para abrir el directorio de trabajo actual en una nueva pestaña.

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

dleavitt
fuente
1
Muy útil. Usando esto en mi .bash_profile, puedo abrir un montón de pestañas y hacer ssh automáticamente. Por supuesto, tengo habilitada la autenticación de par de claves ssh
Sandeep Kanabar
16
osascript -e 'tell app "Terminal"
   do script "echo hello"
end tell'

Esto abre una nueva terminal y ejecuta el comando "echo hello" dentro de ella.

Szymon Morawski
fuente
3
Esto funcionó, pero la nueva pestaña se creó en una instancia separada de Terminal. ¿De todos modos la nueva pestaña permanece en la instancia actual de mi Terminal?
Calvin Cheng
Por cierto, puede usar do script ""con una cadena vacía para crear una nueva terminal sin emitir un comando.
Chris Page
9

Si usa oh-my-zsh (que todo geek de moda debería usar), después de activar el complemento "osx" .zshrc, simplemente ingrese el tabcomando; se abrirá una nueva pestaña y cden el directorio en el que se encontraba.

CharlesB
fuente
Parece muy interesante. ¿Cuál es la diferencia entre zcsh y bash convencional?
Calvin Cheng
Son muy similares, pero lo más interesante es que tiene una función de finalización de pestañas y autocorrección inteligente y potente. Vea una buena comparación aquí . Oh-my-zsh está configurando un entorno con configuraciones / complementos agradables y prácticos para que pueda comenzar
CharlesB
Eché un vistazo rápido al enlace de comparación de CharlesB. Muy interesante. Suena casi como shell BPython versus shell iPython.
Calvin Cheng
zsh se las arregla para mantener aún más cruft viejo para perder el control
James
¿Me puedes proporcionar más información sobre esto? ¿Qué es el comando de tabulación? Entrar tabno parece hacer nada
Solvitieg
7

El atajo de teclado cmd-tabre una nueva pestaña, por lo que puede pasar esta pulsación de tecla al comando OSA de la siguiente manera:

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

Aziz Alto
fuente
6

Los agregué a mi .bash_profile para poder tener acceso a tabname y 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
}

Entonces, cuando esté en una pestaña en particular, simplemente escriba

tabname "New TabName"

para organizar todas las pestañas abiertas que tienes. Es mucho mejor que obtener información en la pestaña y cambiarla allí.

richtera
fuente
Gracias. ¿Sabes cómo conservar el nombre de la pestaña después de hacer un ssh desde la pestaña y salir de la sesión de ssh?
anjanb
4

Sé que esta es una publicación antigua, pero esto funcionó para mí:

open -a Terminal "`pwd`"

Para ejecutar un comando como se solicita a continuación, se necesita algo de jiggery:

echo /sbin/ping 8.8.8.8 > /tmp/tmp.sh;chmod a+x /tmp/tmp.sh;open -a Terminal /tmp/tmp.sh
neófito
fuente
¡Muy agradable! ¿Cómo hago si quiero pasar comandos que se ejecutarán en la nueva instancia de Terminal? : D
Strazan
@Strazan editó la respuesta anterior ... ¡¡diviértete !! Parece que la terminal tomará un parámetro como ese ...
neófito
3

cuando está en una ventana de terminal, el comando + n => abre un nuevo terminal y el comando + t => abre una nueva pestaña en la ventana del terminal actual

xdev
fuente
1
esto tiene que funcionar desde la línea de comandos. básicamente un guión. porque es una tarea repetitiva
Gianfranco P.
2

Si está utilizando iTerm, este comando abrirá una nueva pestaña:

osascript -e 'tell application "iTerm" to activate' -e 'tell application "System Events" to tell process "iTerm" to keystroke "t" using command down'
Andrew Schreiber
fuente
Si necesita agregar esto a un .zshrc o .bashrc, puede hacerlo con una función en lugar de un alias (debido a todos los escapes que tendrá que hacer). stackoverflow.com/a/20111135/1401336
Vigrant
@Andrew Schreiber: Pero el control no se transfiere a la nueva pestaña. Quiero decir, si tiene algún código después de abrir la nueva pestaña, ese código se ejecuta en la pestaña original. ¿Hay alguna forma de decirle al script que procese los siguientes comandos en la nueva pestaña?
Ashwin
1
open -n -a Terminal

y puede pasar el directorio de destino como parámetro

open -n -a Terminal /Users
Everton Santos
fuente
Esto abre una nueva ventana para mí. No es una pestaña.
stack-delay
0

¿Qué pasa con este fragmento simple, basado en un comando de script estándar (echo):

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

Con X instalado (por ejemplo, de homebrew o Quartz), un simple "xterm &" hace (casi) el truco, abre una nueva ventana de terminal (aunque no una pestaña).

Immanuel Kant
fuente