Otwórz nową kartę Terminal z wiersza poleceń (Mac OS X)

116

Czy można otworzyć nową kartę w terminalu Mac OS X z wiersza poleceń w aktualnie otwartej karcie?

Wiem, że skrót klawiaturowy otwierający nową kartę w Terminalu to „CMD + t”, ale szukam rozwiązania opartego na skryptach, wykonywanego w wierszu poleceń.

Calvin Cheng
źródło

Odpowiedzi:

126

Spróbuj tego:

osascript -e 'tell application "Terminal" to activate' -e 'tell application "System Events" to tell process "Terminal" to keystroke "t" using command down'
Gordon Davisson
źródło
D'Oh! Całkowicie przegapiłem Twój komentarz, znalazłem podobne rozwiązanie przez Google. Jedna różnica: nie działało to dla mnie (w 10.6.8), chyba że Terminal był pierwszą aplikacją, więc dodałem „aktywuj”, aby wymusić na pierwszym miejscu.
Gordon Davisson,
5
edycja: Jak umieścić nowe polecenie ex echo hellow tej nowej karcie.
ThomasReggi
22
@ThomasReggi: Dodaj -e 'tell application "Terminal" to do script "echo hello" in selected tab of the front window'na końcu polecenia osascript.
Gordon Davisson
2
@clevertension dla iTerm to po prostuopen -a iTerm ~/Applications/
onmyway133
1
@Ciastopiekarz Czy masz na myśli nowo otwartą zakładkę? Zastosuj to samo podejście, co moja odpowiedź dla ThomasReggi: dodaj -e 'tell application "Terminal" to do script "cd /path/to/target/directory" in selected tab of the front window'. Zwróć uwagę, że jeśli ścieżka pochodzi ze zmiennej, będziesz musiał użyć łańcucha w podwójnych cudzysłowach zamiast w apostrofach i uciec od wewnętrznego cudzysłowu i prawdopodobnie samej ścieżki.
Gordon Davisson
163

Aktualizacja : Ta odpowiedź zyskała popularność dzięki funkcji powłoki zamieszczonej poniżej, która nadal działa od wersji OSX 10.10 (z wyjątkiem -gopcji).
Jednak bardziej funkcjonalna, solidniejsza, przetestowana wersja skryptu jest teraz dostępna w rejestrze npm jako CLIttab , która obsługuje również iTerm2 :

  • Jeśli masz zainstalowany Node.js , po prostu uruchom:

    npm install -g ttab
    

    (w zależności od tego, jak zainstalowałeś Node.js, może być konieczne dodanie przedrostka sudo).

  • W przeciwnym razie postępuj zgodnie z tymi instrukcjami .

  • Po zainstalowaniu uruchom, ttab -haby uzyskać zwięzłe informacje o użytkowaniu lub man ttabwyświetlić instrukcję.


Opierając się na zaakceptowanej odpowiedzi, poniżej znajduje się wygodna funkcja bash do otwierania nowej karty w bieżącym oknie Terminala i opcjonalnie wykonywania polecenia (jako bonus, zamiast tego dostępna jest opcja wariantu do tworzenia nowego okna ).

Jeśli określono polecenie, jego pierwszy token zostanie użyty jako tytuł nowej karty.

Przykładowe wywołania:

    # 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

OSTRZEŻENIE : Po uruchomieniu newtab(lub newwin) ze skryptu początkowy folder roboczy skryptu będzie folderem roboczym w nowej karcie / oknie, nawet jeśli zmienisz folder roboczy w skrypcie przed wywołaniem newtab/ newwin- pass evalza pomocą cdpolecenia jako obejścia (patrz przykład powyżej).

Kod źródłowy (na przykład wklej do swojego profilu 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
źródło
3
@jcollum My pleasure; cieszę się, że jest to przydatne. Właśnie zaktualizowałem post z zastrzeżeniem dotyczącym folderów roboczych, a także zaktualizowałem kod: dodane opcje -g(nie aktywuj Terminala podczas tworzenia nowej karty / okna) i -G(nie aktywuj terminala i nie zmieniaj aktywnej karty w terminalu) ) - pomocne np. przy uruchamianiu serwera w tle. Zwróć uwagę, że tworząc w ten sposób nową kartę, Terminal nadal musi zostać aktywowany na krótko przed ponownym aktywowaniem poprzednio aktywnej aplikacji.
mklement0
1
@Leonardo Nowa karta ma ten sam katalog roboczy, co karta, z której funkcja została wywołana. Zmiana na inny folder w skrypcie przed wywołaniem newtabniestety NIE działa. Sposób obejścia problemu polega na przekazaniu evalinstrukcji z cdpoleceniem do newtab; np newtab eval "cd ~/Library/Application\ Support; ls". : . Cytuj dwukrotnie całe polecenie przekazane do evali użyj wewnątrz znaku odwrotnego ukośnika.
mklement0
1
@IntegrityFirst: Zgodnie z twoją sugestią, zmieniłem podpisy funkcji na function newtabi function newwin(jednak BEZ nawiasów), aby uniknąć kolizji z aliasami podczas definiowania funkcji, ale pamiętaj, że przy wywołaniu alias o tej samej nazwie ma pierwszeństwo (do ominąć alias, ad-hoc, zacytować dowolną część nazwy funkcji, np \newtab.:).
mklement0
2
@IntegrityFirst: Oto co dowiedziałem się: Korzystanie z POSIX <name>() { ... }składni funkcji sprawia <name>podlegającego ekspansji alias , który łamie definicję funkcji, jeśli alias (parsowania błąd!) <name>Dzieje się zdefiniować. Zwykle nie jest to problem, ponieważ w normalnie wywoływanych skryptach rozwijanie aliasów jest domyślnie wyłączone. Jednak w skryptach ŹRÓDŁOWYCH z powłoki INTERAKTYWNEJ - na przykład w plikach profili / inicjalizacyjnych - rozszerzanie aliasów JEST włączone. Poprawka: Użyj function <name> { ... } składni innej niż POSIX do zdefiniowania funkcji - <name>NIE podlega wówczas rozszerzaniu aliasów.
mklement0
1
Dzięki! To dodaje if [ "${BASH_SOURCE}" == "${0}" ]oświadczenie przypadku tak to można nazwać jako skrypt (np newtab.sh, newwin.sh): gist.github.com/westurner/01b6be85e5a51fda22a6
Wes Turner
18

Oto jak to robi 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
}

Po dodaniu tego do swojego .bash_profile, użyjesz tabpolecenia, aby otworzyć bieżący katalog roboczy w nowej karcie.

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

dleavitt
źródło
1
Bardzo pomocne. Używając tego w moim .bash_profile, mogę automatycznie uruchomić kilka zakładek i ssh do nich. Oczywiście, mam włączone uwierzytelnianie par kluczy ssh
Sandeep Kanabar
16
osascript -e 'tell app "Terminal"
   do script "echo hello"
end tell'

Spowoduje to otwarcie nowego terminala i wykonanie w nim polecenia „echo hello”.

Szymon Morawski
źródło
3
To zadziałało, ale nowa karta została utworzona w oddzielnej instancji Terminala. Czy w ogóle nowa karta pozostaje w bieżącej instancji mojego terminala?
Calvin Cheng
Nawiasem mówiąc, możesz użyć do script ""z pustym ciągiem, aby utworzyć nowy terminal bez wydawania polecenia.
Chris Page,
9

Jeśli używasz oh-my-zsh (z którego powinien korzystać każdy modny maniak), po aktywacji wtyczki "osx" .zshrcpo prostu wpisz tabpolecenie; otworzy nową kartę i cdkatalog, w którym byłeś.

Charles B.
źródło
Wygląda to bardzo interesująco. Jaka jest różnica między zcsh a konwencjonalnym bashem?
Calvin Cheng
Są bardzo podobne, ale co najciekawsze, ma inteligentne, potężne uzupełnianie kart i automatyczną korektę. Zobacz dobre porównanie tutaj . Oh-my-zsh tworzy środowisko z przyjemnymi i przydatnymi ustawieniami / wtyczkami, które
pomogą
Rzuciłem okiem na link porównawczy Charlesa B. Bardzo interesujące. Brzmi prawie jak powłoka BPythona w porównaniu z powłoką iPythona.
Calvin Cheng,
zsh udaje się zachować jeszcze bardziej stary cruft, aby stracić kontrolę
James
Czy możesz podać więcej informacji na ten temat? Co to jest polecenie tab? Wejście tabnie wydaje się nic robić
Solvitieg
7

Skrót klawiaturowy cmd-totwiera nową kartę, więc możesz przekazać to naciśnięcie klawisza do polecenia OSA w następujący sposób:

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

Aziz Alto
źródło
6

Dodałem je do mojego .bash_profile, aby mieć dostęp do tabname i 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
}

Więc kiedy jesteś na określonej karcie, możesz po prostu wpisać

tabname "New TabName"

aby uporządkować wszystkie otwarte karty. To znacznie lepsze niż uzyskanie informacji na karcie i jej zmiana.

richtera
źródło
dzięki. czy wiesz, jak zachować nazwę karty po wykonaniu ssh z karty i wyjściu z sesji ssh?
anjanb,
4

Wiem, że to stary post, ale to zadziałało:

open -a Terminal "`pwd`"

Aby wykonać polecenie zgodnie z poniższym żądaniem, trzeba trochę popracować:

echo /sbin/ping 8.8.8.8 > /tmp/tmp.sh;chmod a+x /tmp/tmp.sh;open -a Terminal /tmp/tmp.sh
neofita
źródło
Bardzo dobrze! Jak to zrobić, jeśli chcę przekazywać polecenia, które będą działać w nowej instancji Terminala? : D
Strazan
@Strazan zredagowana odpowiedź powyżej ... baw się dobrze !! Wygląda na to, że terminal przyjmie taki parametr ...
neofita
3

gdy jesteś w oknie terminala, polecenie + n => otwiera nowy terminal, a polecenie + t => otwiera nową kartę w bieżącym oknie terminala

xdev
źródło
1
to musi działać z linii poleceń. w zasadzie skrypt. ponieważ jest to powtarzalne zadanie
Gianfranco P.
2

Jeśli używasz iTerm, to polecenie otworzy nową kartę:

osascript -e 'tell application "iTerm" to activate' -e 'tell application "System Events" to tell process "iTerm" to keystroke "t" using command down'
Andrew Schreiber
źródło
Jeśli chcesz dodać to do .zshrc lub .bashrc, możesz to zrobić za pomocą funkcji zamiast aliasu (ze względu na wszystkie znaki ucieczki, które będziesz musiał zrobić). stackoverflow.com/a/20111135/1401336
Vigrant
@Andrew Schreiber: Ale kontrola nie jest przenoszona do nowej karty. To znaczy, jeśli masz jakiś kod po otwarciu nowej karty, ten kod zostanie wykonany na oryginalnej karcie. Czy istnieje sposób, aby poinstruować skrypt, aby przetwarzał następujące polecenia w nowej karcie?
Ashwin
1
open -n -a Terminal

i możesz przekazać katalog docelowy jako parametr

open -n -a Terminal /Users
Everton Santos
źródło
Otworzy się dla mnie nowe okno. To nie jest karta.
opóźnienie stosu
0

A co z tym prostym fragmentem, opartym na standardowym poleceniu skryptu (echo):

# set mac osx's terminal title to "My Title"
echo -n -e "\033]0;My Title\007"
Adrien Joly
źródło
0

Po zainstalowaniu X-ów (np. Z homebrew lub Quartz), proste "xterm &" załatwia (prawie) sprawę, otwiera nowe okno terminala (ale nie zakładkę).

Immanuel Kant
źródło