OS X Terminal.app: come avviare una nuova scheda nella stessa directory della scheda corrente?


24

Ho spesso bisogno di aprire una nuova scheda nella stessa directory della mia scheda corrente per fare qualcos'altro mentre la mia scheda corrente è occupata da un lungo processo. Tuttavia, per impostazione predefinita quando si crea una nuova scheda, Terminal.app inizia da ~ /. Qualche idea su come farlo saltare automaticamente?


Grazie ragazzi per molte risposte rapide! Sto bene lanciando una nuova scheda invocando uno script, ma mi chiedevo se ci fosse un altro modo per farlo poiché non sono in grado di eseguire uno script se c'è già un programma in esecuzione e che occupa la scheda corrente: |
Riobard,

Risposte:


10

In OS X 10.7 (Lion), Terminal.app lo supporta nativamente: New Windows/Tabs open in: Same working directory


Peccato che Apple non faccia backport ... mi piacerebbe vedere questa funzionalità in Snow Leopard.
Passeggia per Nauta il

4
Ho impostato ma non funziona per me. La finestra delle preferenze dice qualcosa sull'abilitazione delle sequenze di escape prima che ciò possa funzionare.
Ram on Rails,

2

Bisogna stare molto attenti quando si passano le stringhe attraverso ambienti diversi.

Corro 10.4, quindi il mio script 'tfork' apre sempre una nuova finestra. Dovrebbe essere facile adattarlo per usare una scheda:

#!/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

Esempio: tfork git log -p ..FETCH_HEAD


Modifica: cwd di un processo già in corso che "occupa" una scheda Terminale

L'idea di "l'attuale directory del programma che occupa la scheda corrente" non è così ovvia come ci si potrebbe aspettare.

Ogni scheda Terminale ha un singolo dispositivo tty che viene utilizzato dai processi che esegue (inizialmente, una shell; successivamente, qualunque cosa avvenga la shell).

Ogni terminale (normale) tty ha un singolo gruppo di processi in primo piano che si potrebbe considerare come "occupare" il tty.

Ogni gruppo di processi può contenere più processi.

Ogni processo può avere la propria directory di lavoro corrente (cwd) (alcuni ambienti danno a ogni thread il proprio cwd o equivalente cwd, ma lo ignoreremo).

I fatti precedenti stabiliscono un tipo di traccia che da tty a cwd: tty -> gruppo di processi in primo piano -> processi del gruppo di processi in primo piano -> cwds.

La prima parte (dai processi tty a primo piano) del problema può essere risolta con l'output di ps :

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

(dove "ttyp6" è il nome del tty di interesse)

Il mapping da process (PID) a cwd può essere effettuato con lsof :

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

(dove "2515,2516" è un elenco separato da virgole dei processi di interesse)

Ma sotto Tiger, non vedo alcun modo diretto per ottenere il nome del dispositivo tty di una particolare finestra Terminale . C'è un modo orribilmente brutto di ottenere il nome tty in Tiger. Forse Leopard o Snow Leopard possono fare di meglio.

Ho messo tutto insieme in un AppleScript come questo:

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

Salvalo con Script Editor ( AppleScript Editor in Snow Leopard) e usa un launcher (ad esempio FastScripts ) per assegnarlo a una chiave (o semplicemente eseguilo dal menu AppleScript (abilitato tramite / Applicazioni / AppleScript / AppleScript Utility.app )).


1

Ho pubblicato uno script che utilizza il codice di Chris Johnsen sopra e un altro script per aprire la nuova scheda nella directory corrente con le impostazioni correnti, principalmente perché coordino i colori dei miei terminali. Grazie Chris, per quella sceneggiatura, lo sto usando da alcuni mesi ed è un grande risparmio di tempo.

(* Questo script apre una nuova scheda Terminal.app nella directory della scheda corrente con le stesse impostazioni. Se non lo hai già fatto, dovrai abilitare l'accesso per i dispositivi di assistenza come descritto qui: http: // www .macosxautomation.com / AppleScript / uiscripting / index.html

È quasi tutto il lavoro di due script messi insieme, grazie a loro:

Lo script di Chris Johnsen apre una nuova scheda nella directory corrente: OS X Terminal.app: come avviare una nuova scheda nella stessa directory della scheda corrente?

Il "menu_click" di Jacob Rus mi consente di creare la scheda con le stesse impostazioni, come l'API di Terminal non funziona: http://hints.macworld.com/article.php?story=20060921045743404

Se si modifica il nome di un profilo Terminale, l'API AppleScript restituisce il vecchio nome fino a quando non si riavvia l'applicazione, quindi lo script non funzionerà su impostazioni rinominate fino ad allora. Ugh. Inoltre, la necessità di attivare Terminal per eseguire il comando di menu porta in primo piano tutte le finestre del terminale.

*)

-- 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

1

Come menzionato altrove , se si utilizza Oh My Zsh , è sufficiente aggiungere il terminalappplug-in. Nel tuo file .zshrc (supponendo che tu stia già utilizzando il plugin git:

plugins=(terminalapp git)

0

Uso questo script alias / shell per farlo.

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

1
A quanto pare avrà problemi se il cwd contiene determinati caratteri (meta-caratteri della shell e token di controllo; ad esempio una directory con uno spazio al suo interno).
Chris Johnsen,

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.