Ouvrez le nouvel onglet Terminal à partir de la ligne de commande (Mac OS X)

116

Est-il possible d'ouvrir un nouvel onglet dans le terminal de Mac OS X à partir de la ligne de commande dans un onglet actuellement ouvert?

Je sais que le raccourci clavier pour ouvrir un nouvel onglet dans Terminal est "CMD + t" mais je recherche une solution basée sur un script exécutée dans la ligne de commande.

Calvin Cheng
la source

Réponses:

126

Essaye ça:

osascript -e 'tell application "Terminal" to activate' -e 'tell application "System Events" to tell process "Terminal" to keystroke "t" using command down'
Gordon Davisson
la source
D'Oh! J'ai complètement manqué votre commentaire, j'ai trouvé une solution similaire via google. Une différence: cela ne fonctionnait pas pour moi (sur 10.6.8) à moins que Terminal ne soit l'application la plus au premier plan, j'ai donc ajouté le "activer" pour le forcer à l'avant.
Gordon Davisson
5
edit: Comment mettre une nouvelle commande ex echo hellodans ce nouvel onglet.
ThomasReggi
22
@ThomasReggi: ajoutez -e 'tell application "Terminal" to do script "echo hello" in selected tab of the front window'à la fin de la commande osascript.
Gordon Davisson
2
@clevertension pour iTerm c'est justeopen -a iTerm ~/Applications/
onmyway133
1
@Ciastopiekarz Voulez-vous dire dans l'onglet nouvellement ouvert? Utilisez la même approche que ma réponse à ThomasReggi: ajouter -e 'tell application "Terminal" to do script "cd /path/to/target/directory" in selected tab of the front window'. Notez que si le chemin provient d'une variable, vous devrez utiliser une chaîne entre guillemets au lieu de guillemets simples et échapper la chaîne entre guillemets interne, et probablement le chemin lui-même.
Gordon Davisson
163

Mise à jour : Cette réponse a gagné en popularité grâce à la fonction shell publiée ci-dessous, qui fonctionne toujours à partir d'OSX 10.10 (à l'exception de l' -goption).
Cependant, une version de script plus complète, plus robuste et testée est maintenant disponible dans le registre npm en tant que CLIttab , qui prend également en charge iTerm2 :

  • Si Node.js est installé, exécutez simplement:

    npm install -g ttab
    

    (selon la façon dont vous avez installé Node.js, vous devrez peut-être ajouter sudo).

  • Sinon, suivez ces instructions .

  • Une fois installé, exécutez ttab -hpour obtenir des informations d'utilisation concises ou man ttabpour afficher le manuel.


En s'appuyant sur la réponse acceptée, vous trouverez ci-dessous une fonction de commodité bash pour ouvrir un nouvel onglet dans la fenêtre actuelle du Terminal et éventuellement exécuter une commande (en prime, il existe une fonction de variante pour créer une nouvelle fenêtre à la place).

Si une commande est spécifiée, son premier jeton sera utilisé comme titre du nouvel onglet.

Exemples d'appels:

    # 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 : Lorsque vous exécutez newtab(ou newwin) à partir d'un script, le dossier de travail initial du script sera le dossier de travail dans le nouvel onglet / fenêtre, même si vous modifiez le dossier de travail à l'intérieur du script avant d' appeler newtab/ newwin- passer evalavec une cdcommande comme solution de contournement (voir l'exemple ci-dessus).

Code source (coller dans votre profil bash, par exemple):

# 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
la source
3
@jcollum Mon plaisir; heureux que vous le trouviez utile. Je viens de mettre à jour le message avec une mise en garde concernant les dossiers de travail et également mis à jour le code: options ajoutées -g(n'activez pas le terminal lors de la création du nouvel onglet / fenêtre) et -G(n'activez pas le terminal et ne modifiez pas l'onglet actif dans le terminal ) - utile, par exemple, lors du démarrage d'un serveur en arrière-plan. Notez que lors de la création d'un nouvel onglet de cette manière, le Terminal doit encore être activé brièvement avant que l'application précédemment active ne soit réactivée.
mklement0
1
@Leonardo Le nouvel onglet a le même répertoire de travail que l'onglet à partir duquel la fonction a été appelée. Passer à un autre dossier dans un script avant d'appeler newtabne fonctionne malheureusement PAS. La solution de contournement consiste à transmettre une evalinstruction avec une cdcommande à newtab; par exemple: newtab eval "cd ~/Library/Application\ Support; ls". Double-quote la totalité de la commande passée à evalet utilisez l'échappement par barre oblique inverse à l'intérieur.
mklement0
1
@IntegrityFirst: Selon votre suggestion, j'ai changé les signatures des fonctions sur function newtabet function newwin(cependant, PAS de parenthèses), ce qui devrait éviter la collision avec les alias lors de la définition des fonctions, mais notez que lors de l' invocation, un alias du même nom a la priorité (à contourner l'alias, ad-hoc, citer n'importe quelle partie du nom de la fonction, par exemple:) \newtab.
mklement0
2
@IntegrityFirst: Voici ce que j'ai appris: l'utilisation de la <name>() { ... }syntaxe de la fonction POSIX est <name>soumise à l' expansion d'alias , ce qui rompt la définition de la fonction (erreur d'analyse!) Si un alias <name>est défini. Ce n'est généralement pas un problème, car dans les scripts normalement appelés, l'expansion des alias est désactivée par défaut. Cependant, dans les scripts SOURCÉS à partir d'un shell INTERACTIF - comme dans les fichiers de profil / d'initialisation - l'extension d'alias EST activée. Correction: utilisez une function <name> { ... } syntaxe non POSIX pour définir la fonction - <name>n'est alors PAS soumise à l'expansion d'alias.
mklement0
1
Merci! Cela ajoute une if [ "${BASH_SOURCE}" == "${0}" ]avec une déclaration de cas il peut être appelé comme un script (par exemple newtab.sh, newwin.sh): gist.github.com/westurner/01b6be85e5a51fda22a6
Wes Turner
18

Voici comment cela se fait par 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
}

Après avoir ajouté ceci à votre .bash_profile, vous utiliserez la tabcommande pour ouvrir le répertoire de travail actuel dans un nouvel onglet.

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

dleavitt
la source
1
Très utile. En utilisant ceci dans mon .bash_profile, je suis capable de lancer un tas d'onglets et de ssh sur eux automatiquement. Bien sûr, j'ai activé l'authentification par paire de clés ssh
Sandeep Kanabar
16
osascript -e 'tell app "Terminal"
   do script "echo hello"
end tell'

Cela ouvre un nouveau terminal et exécute la commande "echo hello" à l'intérieur.

Szymon Morawski
la source
3
Cela a fonctionné mais le nouvel onglet a été créé dans une instance distincte de Terminal. Y a-t-il de toute façon le nouvel onglet reste dans l'instance actuelle de mon terminal?
Calvin Cheng
En passant, vous pouvez utiliser do script ""avec une chaîne vide pour créer un nouveau terminal sans émettre de commande.
Chris Page
9

Si vous utilisez oh-my-zsh (que tout geek branché devrait utiliser), après avoir activé le plugin "osx" .zshrc, entrez simplement la tabcommande; il ouvrira un nouvel onglet et cddans le répertoire où vous étiez.

CharlesB
la source
Cela semble très intéressant. Quelle est la différence entre zcsh et bash conventionnel?
Calvin Cheng
Ils sont très similaires, mais le plus intéressant est qu'ils ont une complétion d'onglet intelligente et puissante et une correction automatique. Voir la bonne comparaison ici . Oh-my-zsh met en place un environnement avec des paramètres / plugins agréables et pratiques pour vous
aider à
Jetez un coup d'œil au lien de comparaison de CharlesB. Très intéressant. Cela ressemble presque au shell BPython par rapport au shell iPython.
Calvin Cheng
zsh parvient à garder encore plus de vieux crétins pour perdre le contrôle
James
Pouvez-vous fournir plus d'informations à ce sujet? Quelle est la commande tab? Entrer tabne semble rien faire
Solvitieg
7

Le raccourci clavier cmd-touvre un nouvel onglet, vous pouvez donc transmettre cette frappe à la commande OSA comme suit:

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

Aziz Alto
la source
6

Je les ai ajoutés à mon .bash_profile afin que je puisse avoir accès à tabname et 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
}

Ainsi, lorsque vous êtes sur un onglet particulier, vous pouvez simplement taper

tabname "New TabName"

pour organiser tous les onglets ouverts dont vous disposez. C'est bien mieux que d'obtenir des informations sur l'onglet et de les modifier ici.

Richtera
la source
Merci. savez-vous comment conserver le nom de l'onglet après avoir effectué un ssh à partir de l'onglet et quitter la session ssh?
anjanb
4

Je sais que c'est un ancien message, mais cela a fonctionné pour moi:

open -a Terminal "`pwd`"

Pour exécuter une commande comme demandé ci-dessous, il faut du jiggery:

echo /sbin/ping 8.8.8.8 > /tmp/tmp.sh;chmod a+x /tmp/tmp.sh;open -a Terminal /tmp/tmp.sh
néophytte
la source
Très agréable! Comment faire si je veux transmettre des commandes qui s'exécuteront dans la nouvelle instance de Terminal? : D
Strazan le
@Strazan a édité la réponse ci-dessus ... amusez-vous !! On dirait que le terminal prendra un paramètre comme ça ...
neophytte
3

lorsque vous êtes dans une fenêtre de terminal, commande + n => ouvre un nouveau terminal et commande + t => ouvre un nouvel onglet dans la fenêtre actuelle du terminal

xdev
la source
1
cela doit fonctionner à partir de la ligne de commande. essentiellement un script. parce que c'est une tâche répétitive
Gianfranco P.
2

Si vous utilisez iTerm, cette commande ouvrira un nouvel onglet:

osascript -e 'tell application "iTerm" to activate' -e 'tell application "System Events" to tell process "iTerm" to keystroke "t" using command down'
Andrew Schreiber
la source
Si vous avez besoin de l'ajouter à un .zshrc ou un .bashrc, vous pouvez le faire avec une fonction au lieu d'un alias (à cause de toutes les échappées que vous devrez faire). stackoverflow.com/a/20111135/1401336
Vigrant
@Andrew Schreiber: Mais le contrôle ne passe pas au nouvel onglet. Je veux dire que si vous avez du code après avoir ouvert le nouvel onglet, ce code est exécuté dans l'onglet d'origine. Existe-t-il un moyen d'indiquer au script de traiter les commandes suivantes dans le nouvel onglet?
Ashwin
1
open -n -a Terminal

et vous pouvez passer le répertoire cible en paramètre

open -n -a Terminal /Users
Everton Santos
la source
Cela ouvre une nouvelle fenêtre pour moi. Pas un onglet.
stack-delay
0

Qu'en est-il de cet extrait de code simple, basé sur une commande de script standard (echo):

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

Avec X installé (par exemple depuis homebrew, ou Quartz), un simple "xterm &" fait (presque) l'affaire, il ouvre une nouvelle fenêtre de terminal (pas un onglet, cependant).

Immanuel Kant
la source