Freigeben über


Shell-Integration

Ab Terminal 1.15 Preview unterstützt das Windows-Terminal einige Features der „Shellintegration“ experimentell. Diese Features erleichtern die Verwendung der Befehlszeile. In früheren Versionen haben wir die Shell aktiviert, um dem Terminal mitzuteilen, was das aktuelle Arbeitsverzeichnis ist. Jetzt haben wir Unterstützung für weitere Sequenzen hinzugefügt, damit Ihre Shell Teile der Terminalausgabe semantisch als „Eingabeaufforderung“, „Befehl“ oder „Ausgabe“ beschreiben kann. Die Shell kann dem Terminal auch mitteilen, ob ein Befehl erfolgreich war oder fehlgeschlagen ist.

Dies ist ein Leitfaden zu einigen der Shellintegrationsfeatures, die wir ab Terminal v1.18 eingeführt haben. Wir planen, in Zukunft noch mehr Features zu erstellen, Daher würden wir uns freuen, zusätzliches Feedback zu erhalten, wie die Benutzer*innen sie verwenden.

Hinweis: Seit Terminal 1.21 sind Markierungen jetzt ein stabiles Feature. Vor 1.21 waren Markierungen nur für Preview -Builds von Terminal aktiviert. Wenn Sie eine Version von Terminal vor 1.21 verwenden, wurde die showMarksOnScrollbar-Einstellung experimental.showMarksOnScrollbar genannt, und autoMarkPrompts wurde experimental.autoMarkPrompts genannt.

Wie funktioniert das?

Die Shellintegration funktioniert, indem die Shell (oder eine beliebige Befehlszeilenanwendung) spezielle „Escapesequenzen“ in das Terminal schreibt. Diese Escapesequenzen werden nicht im Terminal ausgegeben. Stattdessen stellen sie Metadaten-Bits bereit, die das Terminal verwenden kann, um mehr über die Vorgänge in der Anwendung zu erfahren. Indem Sie diese Sequenzen in die Eingabeaufforderung Ihrer Shell einfügen, können Sie die Shell dazu bringen, kontinuierlich Informationen an das Terminal bereitzustellen, die nur die Shell kennt.

Für die folgenden Sequenzen:

  • OSC ist die Zeichenfolge "\x1b]" – ein Escapezeichen gefolgt von ]
  • ST ist das „Zeichenfolgen-Abschlusszeichen“ und kann entweder \x1b\ (ein ESC-Zeichen, gefolgt von \) oder \x7 (das BEL-Zeichen) sein
  • Leerzeichen sind lediglich illustrativer Natur.
  • Zeichenfolgen in <> sind Parameter, die durch einen anderen Wert ersetzt werden sollten.

Die relevanten unterstützten Shellintegrationssequenzen ab Terminal v1.18 sind:

  • OSC 133 ; A ST ("FTCS_PROMPT") – Der Anfang einer Eingabeaufforderung.
  • OSC 133 ; B ST ("FTCS_COMMAND_START") – Der Anfang einer Befehlszeile (READ: das Ende der Eingabeaufforderung).
  • OSC 133 ; C ST ("FTCS_COMMAND_EXECUTED") – Der Anfang der Befehlsausgabe / das Ende der Befehlszeile.
  • OSC 133 ; D ; <ExitCode> ST ("FTCS_COMMAND_FINISHED") – das Ende eines Befehls. ExitCode Wenn ExitCode angegeben wird, behandelt das Terminal 0 als „Erfolg“ und alles andere als Fehler. Wenn nicht angegeben, belässt das Terminal nur die Markierung als Standardfarbe.

Aktivieren von Shellintegrationsmarkierungen

Die Unterstützung dieser Features erfordert eine Zusammenarbeit zwischen Ihrer Shell und dem Terminal. Sie müssen sowohl Einstellungen im Terminal aktivieren, um diese neuen Features verwenden zu können, als auch die Eingabeaufforderung Ihrer Shell ändern.

Um diese Features im Terminal zu aktivieren, sollten Sie Ihren Einstellungen Folgendes hinzufügen:

"profiles":
{
    "defaults":
    {
        // Enable marks on the scrollbar
        "showMarksOnScrollbar": true,

        // Needed for both pwsh, CMD and bash shell integration
        "autoMarkPrompts": true,

        // Add support for a right-click context menu
        // You can also just bind the `showContextMenu` action
        "experimental.rightClickContextMenu": true,
    },
}
"actions":
[
    // Scroll between prompts
    { "keys": "ctrl+up",   "command": { "action": "scrollToMark", "direction": "previous" }, },
    { "keys": "ctrl+down", "command": { "action": "scrollToMark", "direction": "next" }, },

    // Add the ability to select a whole command (or its output)
    { "command": { "action": "selectOutput", "direction": "prev" }, },
    { "command": { "action": "selectOutput", "direction": "next" }, },

    { "command": { "action": "selectCommand", "direction": "prev" }, },
    { "command": { "action": "selectCommand", "direction": "next" }, },
]

Wie Sie diese Markierungen in Ihrer Shell aktivieren, variiert von Shell zu Shell. Im Folgenden finden Sie Lernprogramme für CMD, PowerShell und Zsh.

PowerShell (pwsh.exe)

Wenn Sie Ihre PowerShell-Eingabeaufforderung noch nie geändert haben, sollten Sie zuerst about_Prompts lesen.

Wir müssen Ihre prompt bearbeiten, um sicherzustellen, dass wir das Terminal über das CWD informieren, und die Eingabeaufforderung mit den entsprechenden Markierungen markieren. PowerShell ermöglicht es uns auch, den Fehlercode aus dem vorherigen Befehl in die 133;D Sequenz einzufügen, wodurch das Terminal die Markierung automatisch färben lässt, je nachdem, ob der Befehl erfolgreich war oder fehlgeschlagen ist.

Fügen Sie Ihrem PowerShell-Profil Folgendes hinzu:

$Global:__LastHistoryId = -1

function Global:__Terminal-Get-LastExitCode {
  if ($? -eq $True) {
    return 0
  }
  $LastHistoryEntry = $(Get-History -Count 1)
  $IsPowerShellError = $Error[0].InvocationInfo.HistoryId -eq $LastHistoryEntry.Id
  if ($IsPowerShellError) {
    return -1
  }
  return $LastExitCode
}

function prompt {

  # First, emit a mark for the _end_ of the previous command.

  $gle = $(__Terminal-Get-LastExitCode);
  $LastHistoryEntry = $(Get-History -Count 1)
  # Skip finishing the command if the first command has not yet started
  if ($Global:__LastHistoryId -ne -1) {
    if ($LastHistoryEntry.Id -eq $Global:__LastHistoryId) {
      # Don't provide a command line or exit code if there was no history entry (eg. ctrl+c, enter on no command)
      $out += "`e]133;D`a"
    } else {
      $out += "`e]133;D;$gle`a"
    }
  }


  $loc = $($executionContext.SessionState.Path.CurrentLocation);

  # Prompt started
  $out += "`e]133;A$([char]07)";

  # CWD
  $out += "`e]9;9;`"$loc`"$([char]07)";

  # (your prompt here)
  $out += "PWSH $loc$('>' * ($nestedPromptLevel + 1)) ";

  # Prompt ended, Command started
  $out += "`e]133;B$([char]07)";

  $Global:__LastHistoryId = $LastHistoryEntry.Id

  return $out
}

Oh Mein Posh-Setup

Oh-my-posh? Sie möchten die oben genannten Elemente leicht ändern, um die ursprüngliche Eingabeaufforderung zu entfernen, und fügen Sie sie dann in der Mitte der Escapesequenzen für die Shellintegration wieder hinzu.

# initialize oh-my-posh at the top of your profile.ps1
oh-my-posh init pwsh --config "$env:POSH_THEMES_PATH\gruvbox.omp.json" | Invoke-Expression
# then stash away the prompt() that oh-my-posh sets
$Global:__OriginalPrompt = $function:Prompt

function Global:__Terminal-Get-LastExitCode {
  if ($? -eq $True) { return 0 }
  $LastHistoryEntry = $(Get-History -Count 1)
  $IsPowerShellError = $Error[0].InvocationInfo.HistoryId -eq $LastHistoryEntry.Id
  if ($IsPowerShellError) { return -1 }
  return $LastExitCode
}

function prompt {
  $gle = $(__Terminal-Get-LastExitCode);
  $LastHistoryEntry = $(Get-History -Count 1)
  if ($Global:__LastHistoryId -ne -1) {
    if ($LastHistoryEntry.Id -eq $Global:__LastHistoryId) {
      $out += "`e]133;D`a"
    } else {
      $out += "`e]133;D;$gle`a"
    }
  }
  $loc = $($executionContext.SessionState.Path.CurrentLocation);
  $out += "`e]133;A$([char]07)";
  $out += "`e]9;9;`"$loc`"$([char]07)";
  
  $out += $Global:__OriginalPrompt.Invoke(); # <-- This line adds the original prompt back

  $out += "`e]133;B$([char]07)";
  $Global:__LastHistoryId = $LastHistoryEntry.Id
  return $out
}

Eingabeaufforderung

Die Eingabeaufforderung stammt aus der Umgebungsvariable PROMPT . CMD.exe liest $e als ESC-Zeichen. Leider hat CMD.exe keine Möglichkeit, den Rückgabecode des vorherigen Befehls in der Eingabeaufforderung abzurufen, sodass wir keine Erfolgs-/Fehlerinformationen in CMD-Eingabeaufforderungen bereitstellen können.

Sie können die Eingabeaufforderung für die aktuelle CMD.exe-Instanz ändern, indem Sie Folgendes ausführen:

PROMPT $e]133;D$e\$e]133;A$e\$e]9;9;$P$e\$P$G$e]133;B$e\

Alternativ können Sie die Variable über die Befehlszeile für alle zukünftigen Sitzungen festlegen:

setx PROMPT $e]133;D$e\$e]133;A$e\$e]9;9;$P$e\$P$G$e]133;B$e\

In diesen Beispielen wird davon ausgegangen, dass Ihr aktueller PROMPT nur $P$Gist. Sie können ihre aktuelle Eingabeaufforderung stattdessen wie folgt umschließen:

PROMPT $e]133;D$e\$e]133;A$e\$e]9;9;$P$e\%PROMPT%$e]133;B$e\

Bash

Sie können das folgende Skript auf eine aktive Shell mit source oder integrierten . Befehlen beziehen oder bash es am Ende Ihrer ${HOME}/.bash_profile (für Anmeldeshells) oder ${HOME}/.bashrc (für Nicht-Login-Shells) hinzufügen, um eine vollständige Shellintegration in bash Versionen zu aktivieren, die größer oder gleich sind bash-4.4 (wobei die PS0 integrierte Variable anfänglich implementiert wurde). Die vollständige Shell-Integration bedeutet, dass jedes angekündigte Terminalfeature wie entworfen funktioniert.

Hinweis

Es sollte darauf hingewiesen werden, dass, wenn PROMPT_COMMAND, PS0, PS1 oder PS2 Variablen bereits auf nicht standardmäßige Werte zugewiesen sind, dies zu unvorhersehbaren Ergebnissen führen kann. Es wäre besser, das Skript zuerst mit der "sauberen" Shell zu testen, indem Sie env --ignore-environment bash --noprofile --norc ausführen und die beschriebene Datei sourcen, wie zuvor angegeben.

# .bash_profile | .bashrc

function __set_ps1() {
    local PS1_TMP="${__PS1_BASE}"
    if [ ! -z "${__IS_WT}" ]; then
        local __FTCS_CMD_FINISHED='\e]133;D;'"${1}"'\e\\'
        PS1_TMP="\[${__FTCS_CMD_FINISHED}\]${__PS1_BASE}"
    fi
    printf '%s' "${PS1_TMP}"
}

function __prompt_command() {
    # Must be first in the list otherwise the exit status will be overwritten.
    local PS1_EXIT_STATUS=${?}
    PS1="$(__set_ps1 ${PS1_EXIT_STATUS})"
}

# ---------------------------------------------------------------------------
# PROMPT (PS0..PS2).

# The given variable might be linked to a function detecting whether `bash`
# actually runs under `Microsoft Terminal` otherwise unexpected garbage might
# be displayed on the user screen.
__IS_WT='true'

printf -v __BASH_V '%d' ${BASH_VERSINFO[*]:0:2}

if [ ${__BASH_V} -ge 44 ]; then
    __PS0_BASE=''
fi

# The following assignments reflect the default values.
__PS1_BASE='\s-\v\$ '
__PS2_BASE='> '

if [ ! -z "${__IS_WT}" ]; then
    __FTCS_PROMPT='\e]133;A\e\\'
    __FTCS_CMD_START='\e]133;B\e\\'
    if [ ${__BASH_V} -ge 44 ]; then
        __FTCS_CMD_EXECUTED='\e]133;C\e\\'
        __PS0_BASE="\[${__FTCS_CMD_EXECUTED}\]"
    fi
    __PS1_BASE="\[${__FTCS_PROMPT}\]${__PS1_BASE}\[${__FTCS_CMD_START}\]"
    # Required, otherwise the `PS2` prefix will split and corrupt a long
    # command.
    __PS2_BASE=''
fi

PROMPT_COMMAND=__prompt_command

if [ ${__BASH_V} -ge 44 ]; then
    PS0="${__PS0_BASE}"
fi
# `PS1` is set with the `__prompt_command` function call.
PS2="${__PS2_BASE}"

Dadurch wird die gesamte Auswahl von bash Eingabeaufforderungsvariablen (PS0, PS1 und PS2) mit den erforderlichen Sequenzen umbrochen, um die vollständige Integration in die Shell zu ermöglichen.

Darüber hinaus kann auch eine Anpassung von ${HOME}/.inputrc erforderlich sein, um die Benachrichtigung im Bearbeitungsmodus und die Markierungen für geänderte Zeilen zu entfernen.

# .inputrc

set mark-modified-lines Off
set show-mode-in-prompt Off

So sollte es aussehen, wenn alles richtig ausgeführt wird:

$ env --ignore-environment bash --noprofile --norc
bash-5.2$ . /tmp/msft-terminal-bash.sh
bash-5.2$ echo "|${PS0}|"
|\[\e]133;C\e\\\]|
bash-5.2$ echo "|${PS1}|"
|\[\e]133;D;0\e\\\]\[\e]133;A\e\\\]\s-\v\$ \[\e]133;B\e\\\]|
bash-5.2$ echo "|${PS2}|"
||

Hinweis: Sehen Sie Ihre bevorzugte Shell hier nicht? Wenn Sie eine Möglichkeit finden, können Sie gerne eine Lösung für Ihre bevorzugte Shell beitragen!

Shell-Integrationsfunktionen

Öffnen neuer Registerkarten im selben Arbeitsverzeichnis

Öffnen neuer Registerkarten im selben Arbeitsverzeichnis

Anzeigen von Markierungen für jeden Befehl in der Bildlaufleiste

Anzeigen von Markierungen für jeden Befehl in der Bildlaufleiste

Automatisches Springen zwischen Befehlen

Dies verwendet die scrollToMark-Aktionen, wie sie oben definiert wurden.

Automatisches Springen zwischen Befehlen

Auswählen der gesamten Ausgabe eines Befehls

In dieser GIF-Datei verwenden wir die selectOutput-Aktion, die an ctrl+g gebunden ist, um die gesamte Ausgabe eines Befehls auszuwählen. Auswählen der gesamten Ausgabe eines Befehls

Im Folgenden wird die experimental.rightClickContextMenu-Einstellung verwendet, um im Terminal ein Kontextmenü zu aktivieren, das mit der rechten Maustaste aufgerufen werden kann. Damit und mit aktivierter Shell-Integration können Sie mit der rechten Maustaste auf einen Befehl klicken, um den gesamten Befehl oder seine Ausgabe auszuwählen.

Auswählen des Befehls über das Kontextmenü (Rechtsklick)

Vorschläge der zuletzt verwendeten Befehle

Wenn die Shell-Integration aktiviert ist, kann die Vorschläge-Benutzeroberfläche so konfiguriert werden, dass auch Ihre zuletzt verwendeten Befehle angezeigt werden.

Die Vorschläge-Benutzeroberfläche mit zuletzt verwendeten Befehlen

Sie können dieses Menü mit der folgenden Aktion öffnen:

{
    "command": { "action": "showSuggestions", "source": "recentCommands", "useCommandline": true },
},

(Weitere Informationen finden Sie in der Dokumentation zu Vorschlägen.)

Zusätzliche Ressourcen