OS X Terminal.app: comment démarrer un nouvel onglet dans le même répertoire que l'onglet actuel?

24

J'ai souvent besoin d'ouvrir un nouvel onglet dans le même répertoire que mon onglet actuel pour faire autre chose alors que mon onglet actuel est occupé par un long processus. Cependant, par défaut lorsque vous créez un nouvel onglet, Terminal.app démarre à ~ /. Une idée de comment le faire sauter automatiquement?

Riobard
la source
Merci les gars pour les nombreuses réponses rapides! Je suis d'accord pour lancer un nouvel onglet en invoquant un script, mais je me demandais s'il y avait une autre façon de le faire car je ne pouvais pas exécuter un script s'il y avait déjà un programme en cours d'exécution et occupant l'onglet en cours: |
Riobard

Réponses:

10

Dans OS X 10.7 (Lion), Terminal.app prend en charge cela nativement: New Windows/Tabs open in: Same working directory

Alistair A. Israël
la source
C'est dommage qu'Apple ne fasse pas de rétroportages ... aimerait voir cette fonctionnalité dans Snow Leopard.
Wander Nauta
4
Je l'ai réglé mais ne fonctionne pas pour moi. La fenêtre des préférences dit quelque chose sur l'activation des séquences d'échappement avant que cela puisse fonctionner.
Ram on Rails
2

Il faut être très prudent lors du passage de chaînes dans différents environnements.

Je lance 10.4, donc mon script 'tfork' ouvre toujours une nouvelle fenêtre à la place. Il devrait être facile de l'adapter pour utiliser un onglet:

#!/bin/sh

# source: http://www.pycs.net/bob/weblog/2004/02/23.html#P49
# Rewritten to use osascript args -> run handler args.
# Added ability to pass additional initial command and args to new shell.
#    Bug: Non ASCII characters are unreliable on Tiger.
#         Tiger's osascript seems to expect args to be encoded in
#         the system's primary encoding (e.g. MacRoman).
#         Once in AppleScript, they are handled OK. Terminal sends them
#         back to the shell as UTF-8.

test $# -eq 0 && set -- : # default command if none given
osascript - "$(pwd)" "$@" <<\EOF
on run args
  set dir to quoted form of (first item of args)
  set cmd_strs to {}
  repeat with cmd_str in rest of args
    set end of cmd_strs to quoted form of cmd_str
  end
  set text item delimiters to " "
  set cmd to cmd_strs as Unicode text
  tell app "Terminal" to do script "cd " & dir & " && " & cmd
end
EOF

Exemple: tfork git log -p ..FETCH_HEAD


Amendement: CWW d'un processus déjà en cours «occupant» un onglet Terminal

L'idée du «répertoire actuel du programme occupant l'onglet actuel» n'est pas aussi évidente qu'on pourrait s'y attendre.

Chaque onglet Terminal possède un seul périphérique tty qui est utilisé par les processus qu'il exécute (initialement, un shell; ensuite, quel que soit le shell qui démarre).

Chaque terminal (normal) Terminal possède un seul groupe de processus de premier plan que l'on pourrait considérer comme «occupant» le terminal.

Chaque groupe de processus peut contenir plusieurs processus.

Chaque processus peut avoir son propre répertoire de travail actuel (cwd) (certains environnements donnent à chaque thread leur propre cwd ou équivalent cwd, mais nous l'ignorerons).

Les faits précédents établissent une sorte de piste qui va de tty à cwd: tty -> groupe de processus de premier plan -> processus du groupe de processus de premier plan -> cwds.

La première partie (des processus tty aux processus de premier plan) du problème peut être résolue avec la sortie de ps :

ps -o tty,pid,tpgid,pgid,state,command | awk 'BEGIN{t=ARGV[1];ARGC=1} $1==t && $3==$4 {print $2}' ttyp6

(où «ttyp6» est le nom du tty d'intérêt)

Le mappage du processus (PID) au cwd peut être fait avec lsof :

lsof -F 0n -a -p 2515,2516 -d cwd

(où «2515,2516» est une liste séparée par des virgules des processus d'intérêt)

Mais sous Tiger, je ne vois aucun moyen direct d'obtenir le nom du périphérique tty d'une fenêtre de terminal particulière . Il y a un moyen horriblement laid d'obtenir le nom tty dans Tiger. Peut-être que Leopard ou Snow Leopard peuvent faire mieux.

J'ai tout rassemblé dans un AppleScript comme celui-ci:

on run
    (* Find the tty. *)
    -- This is ugly. But is seems to work on Tiger. Maybe newer releases can do better.
    tell application "Terminal"
        set w to window 1
        tell w
            set origName to name
            set title displays device name to not title displays device name
            set newName to name
            set title displays device name to not title displays device name
        end tell
    end tell
    set tty to extractTTY(origName, newName)
    if tty is "" then
        display dialog "Could not find the tty for of the current Terminal window." buttons "Cancel" cancel button "Cancel" default button "Cancel"
    end if

    (* Find the PIDs of the processes in the foreground process group on that tty. *)
    set pids to paragraphs of (do shell script "
ps -o pid,tty,tpgid,pgid,state,command |
awk '
    BEGIN   {t=ARGV[1];ARGC=1}
    $2==t && $3==$4 {print $1}
' " & quoted form of tty)
    if pids is {} or pids is {""} then
        display dialog "Could not find the processes for " & tty & "." buttons "Cancel" cancel button "Cancel" default button "Cancel"
    end if

    (* Find the unique cwds of those processes. *)
    set text item delimiters to {","}
    set lsof to do shell script "lsof -F 0n -a -d cwd -p " & quoted form of (pids as Unicode text) without altering line endings
    set text item delimiters to {(ASCII character 0) & (ASCII character 10)}
    set cwds to {}
    repeat with lsofItem in text items of lsof
        if lsofItem starts with "n" then
            set cwd to text 2 through end of lsofItem
            if cwds does not contain cwd then ¬
                set end of cwds to cwd
        end if
    end repeat
    if cwds is {} then
        display dialog "No cwds found!?" buttons "Cancel" cancel button "Cancel" default button "Cancel"
    end if
    if length of cwds is greater than 1 then
        set cwds to choose from list cwds with title "Multiple Distinct CWDs" with prompt "Choose the directory to use:" without multiple selections allowed and empty selection allowed
        if cwds is false then error number -128 -- cancel
    end if

    (* Open a new Terminal. *)
    tell application "Terminal" to do script "cd " & quoted form of item 1 of cwds
end run

to extractTTY(a, b)
    set str to textLeftAfterRemovingMatchingHeadAndTail(a, b)
    set offs to offset of "tty" in str
    if offs > 0 then
        return text offs through (offs + 4) of str
    end if
    return ""
end extractTTY
to textLeftAfterRemovingMatchingHeadAndTail(big, little)
    set text item delimiters to space
    if class of big is not list then set big to text items of big
    if class of little is not list then set little to text items of little
    set {maxLen, minLen} to {length of big, length of little}
    if maxLen < minLen then ¬
        set {big, little, maxLen, minLen} to {little, big, minLen, maxLen}

    set start to missing value
    repeat with i from 1 to minLen
        if item i of big is not equal to item i of little then
            set start to i
            exit repeat
        end if
    end repeat
    if start is missing value then
        if maxLen is equal to minLen then
            return ""
        else
            return items (minLen + 1) through end of big as Unicode text
        end if
    end if

    set finish to missing value
    repeat with i from -1 to -minLen by -1
        if item i of big is not equal to item i of little then
            set finish to i
            exit repeat
        end if
    end repeat
    if finish is missing value then set finish to -(minLen + 1)

    return items start through finish of big as Unicode text
end textLeftAfterRemovingMatchingHeadAndTail

Enregistrez-le avec Script Editor ( AppleScript Editor dans Snow Leopard) et utilisez un lanceur (par exemple FastScripts ) pour l'affecter à une clé (ou exécutez-le simplement à partir du menu AppleScript (activé via / Applications / AppleScript / AppleScript Utility.app )).

Chris Johnsen
la source
1

J'ai publié un script qui utilise le code de Chris Johnsen ci-dessus et un autre script pour ouvrir le nouvel onglet dans le répertoire actuel avec les paramètres actuels, principalement parce que je coordonne les couleurs de mes terminaux. Merci Chris, pour ce script, je l'utilise depuis quelques mois maintenant et c'est un excellent gain de temps.

(* Ce script ouvre un nouvel onglet Terminal.app dans le répertoire de l'onglet actuel avec les mêmes paramètres. Vous devrez, si vous ne l'avez pas déjà fait, activer l'accès pour les appareils et accessoires fonctionnels comme décrit ici: http: // www .macosxautomation.com / applescript / uiscripting / index.html

C'est presque tout le travail de deux scripts réunis, merci à eux:

Le script de Chris Johnsen ouvre un nouvel onglet dans le répertoire courant: OS X Terminal.app: comment démarrer un nouvel onglet dans le même répertoire que l'onglet courant?

Le «menu_click» de Jacob Rus me permet de créer l'onglet avec les mêmes paramètres, contrairement à l'API de Terminal: http://hints.macworld.com/article.php?story=20060921045743404

Si vous modifiez le nom d'un profil de terminal, l'API AppleScript renvoie l'ancien nom jusqu'au redémarrage de l'application, de sorte que le script ne fonctionnera pas sur les paramètres renommés jusque-là. Pouah. De plus, la nécessité d'activer Terminal pour exécuter la commande de menu met toutes les fenêtres du terminal au premier plan.

*)

-- from http://hints.macworld.com/article.php?story=20060921045743404
-- `menu_click`, by Jacob Rus, September 2006
-- 
-- Accepts a list of form: `{"Finder", "View", "Arrange By", "Date"}`
-- Execute the specified menu item.  In this case, assuming the Finder 
-- is the active application, arranging the frontmost folder by date.

on menu_click(mList)
    local appName, topMenu, r

    -- Validate our input
    if mList's length < 3 then error "Menu list is not long enough"

    -- Set these variables for clarity and brevity later on
    set {appName, topMenu} to (items 1 through 2 of mList)
    set r to (items 3 through (mList's length) of mList)

    -- This overly-long line calls the menu_recurse function with
    -- two arguments: r, and a reference to the top-level menu
    tell application "System Events" to my menu_click_recurse(r, ((process appName)'s ¬
        (menu bar 1)'s (menu bar item topMenu)'s (menu topMenu)))
end menu_click

on menu_click_recurse(mList, parentObject)
    local f, r

    -- `f` = first item, `r` = rest of items
    set f to item 1 of mList
    if mList's length > 1 then set r to (items 2 through (mList's length) of mList)

    -- either actually click the menu item, or recurse again
    tell application "System Events"
        if mList's length is 1 then
            click parentObject's menu item f
        else
            my menu_click_recurse(r, (parentObject's (menu item f)'s (menu f)))
        end if
    end tell
end menu_click_recurse



-- with the noted slight modification, from /superuser/61149/os-x-terminal-app-how-to-start-a-new-tab-in-the-same-directory-as-the-current-ta/61264#61264

on run
    (* Find the tty. *)
    -- This is ugly. But is seems to work on Tiger. Maybe newer releases can do better.
    tell application "Terminal"
        set w to the front window
        tell w
            set origName to name
            set title displays device name to not title displays device name
            set newName to name
            set title displays device name to not title displays device name
        end tell
    end tell
    set tty to extractTTY(origName, newName)
    if tty is "" then
        display dialog "Could not find the tty for of the current Terminal window." buttons "Cancel" cancel button "Cancel" default button "Cancel"
    end if

    (* Find the PIDs of the processes in the foreground process group on that tty. *)
    set pids to paragraphs of (do shell script "
ps -o pid,tty,tpgid,pgid,state,command |
awk '
    BEGIN   {t=ARGV[1];ARGC=1}
    $2==t && $3==$4 {print $1}
' " & quoted form of tty)
    if pids is {} or pids is {""} then
        display dialog "Could not find the processes for " & tty & "." buttons "Cancel" cancel button "Cancel" default button "Cancel"
    end if

    (* Find the unique cwds of those processes. *)
    set text item delimiters to {","}
    set lsof to do shell script "lsof -F 0n -a -d cwd -p " & quoted form of (pids as Unicode text) without altering line endings
    set text item delimiters to {(ASCII character 0) & (ASCII character 10)}
    set cwds to {}
    repeat with lsofItem in text items of lsof
        if lsofItem starts with "n" then
            set cwd to text 2 through end of lsofItem
            if cwds does not contain cwd then ¬
                set end of cwds to cwd
        end if
    end repeat
    if cwds is {} then
        display dialog "No cwds found!?" buttons "Cancel" cancel button "Cancel" default button "Cancel"
    end if
    if length of cwds is greater than 1 then
        set cwds to choose from list cwds with title "Multiple Distinct CWDs" with prompt "Choose the directory to use:" without multiple selections allowed and empty selection allowed
        if cwds is false then error number -128 -- cancel
    end if

    (* Open a new Terminal. *)

    -- Here is where I substituted the menu_click call to use the current settings

    tell application "Terminal"
        activate
        tell window 1
            set settings to name of current settings in selected tab
        end tell
    end tell
    menu_click({"Terminal", "Shell", "New Tab", settings})

    tell application "Terminal" to do script "cd " & quoted form of item 1 of cwds in selected tab of window 1
end run

to extractTTY(a, b)
    set str to textLeftAfterRemovingMatchingHeadAndTail(a, b)
    set offs to offset of "tty" in str
    if offs > 0 then
        return text offs through (offs + 6) of str
    end if
    return ""
end extractTTY
to textLeftAfterRemovingMatchingHeadAndTail(big, little)
    set text item delimiters to space
    if class of big is not list then set big to text items of big
    if class of little is not list then set little to text items of little
    set {maxLen, minLen} to {length of big, length of little}
    if maxLen < minLen then ¬
        set {big, little, maxLen, minLen} to {little, big, minLen, maxLen}

    set start to missing value
    repeat with i from 1 to minLen
        if item i of big is not equal to item i of little then
            set start to i
            exit repeat
        end if
    end repeat
    if start is missing value then
        if maxLen is equal to minLen then
            return ""
        else
            return items (minLen + 1) through end of big as Unicode text
        end if
    end if

    set finish to missing value
    repeat with i from -1 to -minLen by -1
        if item i of big is not equal to item i of little then
            set finish to i
            exit repeat
        end if
    end repeat
    if finish is missing value then set finish to -(minLen + 1)

    return items start through finish of big as Unicode text
end textLeftAfterRemovingMatchingHeadAndTail
retour arrière
la source
1

Comme mentionné ailleurs , si vous utilisez Oh My Zsh , vous devez simplement ajouter le terminalappplugin. Dans votre fichier .zshrc (en supposant que vous utilisez déjà le plugin git:

plugins=(terminalapp git)
Joshua J. McKinnon
la source
0

J'utilise ce script alias / shell pour le faire.

# modified from http://www.nanoant.com/programming/opening-specified-path-in-terminals-new-tab
alias twd=new_terminal_working_directory
function new_terminal_working_directory() {
osascript <<END 
        tell application "Terminal"
            tell application "System Events" to tell process "Terminal" to keystroke "t" using command down
        do script "cd $(pwd)" in first window
    end tell
END
}
Ted Naleid
la source
1
Cela semble avoir des problèmes si le cwd contient certains caractères (méta-caractères shell et jetons de contrôle; par exemple, un répertoire avec un espace).
Chris Johnsen