Apri una nuova scheda Terminale dalla riga di comando (Mac OS X)


116

È possibile aprire una nuova scheda nel terminale di Mac OS X dalla riga di comando in una scheda attualmente aperta?

So che la scorciatoia da tastiera per aprire una nuova scheda in Terminale è "CMD + t" ma sto cercando una soluzione basata su script eseguita nella riga di comando.

Risposte:


126

Prova questo:

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

D'oh! Ho perso completamente il tuo commento, ho trovato una soluzione simile tramite google. Una differenza: non ha funzionato per me (sulla 10.6.8) a meno che Terminal non fosse l'applicazione in primo piano, quindi ho aggiunto l '"attivazione" per forzarlo in primo piano.
Gordon Davisson

5
modifica: come inserisco un nuovo comando ex echo helloin questa nuova scheda.
ThomasReggi

22
@ThomasReggi: aggiungi -e 'tell application "Terminal" to do script "echo hello" in selected tab of the front window'alla fine del comando osascript.
Gordon Davisson

2
@clevertension per iTerm è soloopen -a iTerm ~/Applications/
onmyway133

1
@Ciastopiekarz Intendi nella scheda appena aperta? Usa lo stesso approccio della mia risposta a ThomasReggi: aggiungi -e 'tell application "Terminal" to do script "cd /path/to/target/directory" in selected tab of the front window'. Nota che se il percorso proviene da una variabile, dovrai usare una stringa tra virgolette doppie invece di virgolette singole ed eseguire l'escape della stringa tra virgolette interne e probabilmente il percorso stesso.
Gordon Davisson

163

Aggiornamento : questa risposta ha guadagnato popolarità in base alla funzione shell pubblicata di seguito, che funziona ancora a partire da OSX 10.10 (ad eccezione -gdell'opzione).
Tuttavia, una versione di script più completa, più robusta e testata è ora disponibile nel registro npm come CLIttab , che supporta anche iTerm2 :

  • Se hai installato Node.js , esegui semplicemente:

    npm install -g ttab
    

    (a seconda di come hai installato Node.js, potresti dover anteporre sudo).

  • Altrimenti, segui queste istruzioni .

  • Una volta installato, esegui ttab -hper ottenere informazioni concise sull'utilizzo o man ttabper visualizzare il manuale.


Basandosi sulla risposta accettata, di seguito è riportata una funzione di convenienza bash per aprire una nuova scheda nella finestra Terminale corrente e opzionalmente eseguire un comando (come bonus, c'è una funzione variante per creare invece una nuova finestra ).

Se viene specificato un comando, il suo primo token verrà utilizzato come titolo della nuova scheda.

Invocazioni di esempio:

    # 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 esegui newtab(o newwin) da uno script, la cartella di lavoro iniziale dello script sarà la cartella di lavoro nella nuova scheda / finestra, anche se cambi la cartella di lavoro all'interno dello script prima di invocare newtab/ newwin- passa evalcon un cdcomando come soluzione alternativa (vedi esempio sopra).

Codice sorgente (incollalo nel tuo profilo bash, ad esempio):

# 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.
}

3
@jcollum Il mio piacere; felice che tu lo trovi utile. Ho appena aggiornato il post con un avvertimento riguardo alle cartelle di lavoro e ho anche aggiornato il codice: opzioni aggiunte -g(non attivare Terminale durante la creazione della nuova scheda / finestra) e -G(non attivare Terminale e non modificare la scheda attiva all'interno di Terminale ) - utile, ad esempio, quando si avvia un server in background. Notare che quando si crea una nuova scheda in questo modo, Terminal deve ancora essere attivato brevemente prima che l'applicazione precedentemente attiva venga riattivata.
mklement0

1
@Leonardo La nuova scheda ha la stessa directory di lavoro della scheda da cui è stata richiamata la funzione. Passare a una cartella diversa all'interno di uno script prima di chiamare newtab, sfortunatamente, NON funziona. La soluzione alternativa è passare evalun'istruzione con un cdcomando a newtab; ad esempio: newtab eval "cd ~/Library/Application\ Support; ls". Applica le virgolette all'intero comando passato evale usa l'escape della barra rovesciata all'interno.
mklement0

1
@ IntegrityFirst: Per tuo suggerimento ho cambiato le firme delle funzioni in function newtabe function newwin(tuttavia, NESSUNA parentesi), in modo che dovrebbe evitare la collisione con gli alias quando si definiscono le funzioni, ma si noti che all'invocazione un alias con lo stesso nome ha la precedenza (per ignora l'alias, ad-hoc, cita qualsiasi parte del nome della funzione, ad esempio:) \newtab.
mklement0

2
@ IntegrityFirst: Ecco cosa ho imparato: l'uso della <name>() { ... }sintassi della funzione POSIX è <name>soggetta all'espansione dell'alias , che interrompe la definizione della funzione (errore di analisi!) Se <name>viene definito un alias . Di solito non è un problema, poiché negli script normalmente richiamati l'espansione degli alias è disattivata per impostazione predefinita. Tuttavia, negli script PROVENIENTI da una shell INTERATTIVA, come nei file di profilo / inizializzazione, l'espansione alias È attivata. Correzione: utilizzare la function <name> { ... } sintassi non POSIX per definire la funzione, <name>quindi NON è soggetta all'espansione dell'alias.
mklement0

1
Grazie! questo aggiunge una if [ "${BASH_SOURCE}" == "${0}" ]con un'istruzione case in modo che possa essere definito come uno script (ad esempio newtab.sh, newwin.sh): gist.github.com/westurner/01b6be85e5a51fda22a6
Wes Turner

18

Ecco come è fatto da 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
}

Dopo averlo aggiunto al tuo .bash_profile, dovresti usare il tabcomando per aprire la directory di lavoro corrente in una nuova scheda.

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


1
Molto utile. Usando questo nel mio .bash_profile, sono in grado di avviare automaticamente un gruppo di schede e ssh. Ovviamente, ho abilitato l'autenticazione della coppia di chiavi ssh
Sandeep Kanabar

16
osascript -e 'tell app "Terminal"
   do script "echo hello"
end tell'

Questo apre un nuovo terminale ed esegue il comando "echo hello" al suo interno.


3
Ha funzionato, ma la nuova scheda è stata creata in un'istanza separata di Terminal. La nuova scheda rimane comunque nell'istanza corrente del mio terminale?
Calvin Cheng

A proposito, puoi usare do script ""con una stringa vuota per creare un nuovo terminale senza emettere un comando.
Chris Page

9

Se usi oh-my-zsh (che ogni fanatico alla moda dovrebbe usare), dopo aver attivato il plugin "osx" .zshrc, inserisci semplicemente il tabcomando; si aprirà una nuova scheda e cdnella directory in cui ti trovavi.


Sembra molto interessante. Qual è la differenza tra zcsh e bash convenzionale?
Calvin Cheng

Sono molto simili, ma la cosa più interessante è che ha un completamento delle schede intelligente e potente e una correzione automatica. Vedi un buon confronto qui . Oh-my-zsh sta impostando un ambiente con impostazioni / plug-in belli e pratici per iniziare
CharlesB

Ho dato una rapida occhiata al link di confronto di CharlesB. Molto interessante. Suona quasi come la shell BPython rispetto alla shell iPython.
Calvin Cheng

zsh riesce a tenere in giro ancora più vecchio cruft per perdere il controllo
James il

Potete fornire maggiori informazioni in merito? Qual è il comando tab? Entrare tabnon sembra fare nulla
Solvitieg

7

La scorciatoia da tastiera cmd-tapre una nuova scheda, quindi puoi passare questa sequenza di tasti al comando OSA come segue:

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


6

Li ho aggiunti al mio .bash_profile così posso avere accesso 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
}

Quindi, quando sei su una scheda particolare, puoi semplicemente digitare

tabname "New TabName"

per organizzare tutte le schede aperte che hai. È molto meglio che ottenere informazioni nella scheda e modificarle lì.


Grazie. sai come mantenere il nome della scheda dopo aver eseguito un ssh dalla scheda ed essere uscito dalla sessione ssh?
anjanb

4

So che questo è un vecchio post, ma per me ha funzionato:

open -a Terminal "`pwd`"

Per eseguire un comando come richiesto di seguito richiede un po 'di jiggery:

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

Molto bella! Come faccio se voglio passare comandi che verranno eseguiti nella nuova istanza di Terminal? : D
Strazan,

@Strazan ha modificato la risposta sopra ... buon divertimento !! Sembra che il terminale prenderà un parametro del genere ...
neophytte

3

quando sei in una finestra di terminale, comando + n => apre un nuovo terminale e comando + t => apre una nuova scheda nella finestra di terminale corrente


1
questo deve funzionare dalla riga di comando. fondamentalmente uno script. perchè è un compito ripetitivo
Gianfranco P.

2

Se stai usando iTerm questo comando aprirà una nuova scheda:

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

Se hai bisogno di aggiungerlo a un .zshrc o .bashrc puoi farlo con una funzione invece di un alias (a causa di tutti gli escape che finirai per dover fare). stackoverflow.com/a/20111135/1401336
Vigrant

@ Andrew Schreiber: Ma il controllo non viene trasferito nella nuova scheda. Voglio dire, se hai del codice dopo aver aperto la nuova scheda, quel codice viene eseguito nella scheda originale. C'è un modo per dire allo script di elaborare i seguenti comandi nella nuova scheda?
Ashwin


0

Che dire di questo semplice frammento, basato su un comando di script standard (echo):

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

0

Con X installato (ad esempio da homebrew o Quartz), un semplice "xterm &" fa (quasi) il trucco, apre una nuova finestra di terminale (non una scheda, però).

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.