如何在外部编辑器中编辑命令行?
How do you edit the command line in an external editor?
tl;博士
我想找到 bash
edit-and-execute-command
小部件或 zsh
edit-command-line
小部件的 Powershell 版本。
背景
短命令直接在命令行上执行,长而复杂的命令从脚本中执行。但是,在它们变得“长”之前,能够在命令行上测试中等长度的命令会有所帮助。为了协助完成这项工作,在外部编辑器中编辑命令变得非常有用。 AFAIK Powershell 本身不支持这个,例如bash
和 zsh
可以。
我目前的尝试
我是 Powershell 的新手,所以我肯定会犯很多错误,但我已经使用 [Microsoft.Powershell.PSConsoleReadLine]
的功能提出了一个可行的解决方案class。我可以将当前命令行复制到文件,编辑文件,然后将编辑后的版本重新注入命令行:
Set-PSReadLineKeyHandler -Chord "Alt+e" -ScriptBlock {
$CurrentInput = $null
# Copy current command-line input, save it to a file and clear it
[Microsoft.PowerShell.PSConsoleReadLine]::GetBufferState([ref] $CurrentInput, [ref] $null)
Set-Content -Path "C:\Temp\ps_${PID}.txt" -Value "$CurrentInput"
[Microsoft.PowerShell.PSConsoleReadLine]::KillRegion()
# Edit the command with gvim
Start-Job -Name EditCMD -ScriptBlock { gvim "C:\Temp\ps_${Using:PID}.txt" }
Wait-Job -Name EditCMD
# Get command back from file the temporary file and insert it into the command-line
$NewInput = (Get-Content -Path "C:\Temp\ps_${PID}.txt") -join "`n"
[Microsoft.PowerShell.PSConsoleReadLine]::Insert($NewInput)
}
问题
我目前的解决方案感觉笨拙而且有些脆弱。
还有其他解决方案吗?目前的解决方案可以改进吗?
环境
- OS Windows 10.0.19043.0
- Powershell 版本 5.1.19041.1320
- PSReadLine 版本 2.0.0
我采用的解决方案
创建类似于 mklement0 所示的“烘焙”可执行文件。我更喜欢 vim
而不是 `gvim,因为它直接在控制台中运行:
'@vim -f %*' > psvim.cmd
$env:EDITOR = "psvim"
Set-PSReadLineKeyHandler -Chord "Alt+e" -Function ViEditVisually
这段代码有一些问题:
- 临时路径是硬编码的,它应该使用
$env:temp
或更好的 [IO.Path]::GetTempPath()
(为了 cross-platform 兼容性)。
- 编辑行后,不会替换整行,只替换光标左侧的文本。如 mklement0, we can simply replace 所述,现有缓冲区而不是擦除它,这解决了问题。
- 当为编辑器使用正确的参数时,不需要创建作业来等待它。对于 VSCode,这是
--wait
(-w
) and for gvim this is --nofork
(-f
),它可以防止这些进程与控制台进程分离,因此 PowerShell 代码会一直等到用户关闭编辑器。
- 关闭编辑器后临时文件没有被删除
这是我修复代码的尝试。我不用gvim
,所以用VSCodecode.exe
测试了一下。下面的代码也包含 gvim
的注释行(已由 OP 确认工作)。
Set-PSReadLineKeyHandler -Chord "Alt+e" -ScriptBlock {
$CurrentInput = $null
# Copy current console line
[Microsoft.PowerShell.PSConsoleReadLine]::GetBufferState([ref] $CurrentInput, [ref] $null)
# Save current console line to temp file
$tempFilePath = Join-Path ([IO.Path]::GetTempPath()) "ps_$PID.ps1"
Set-Content $tempFilePath -Value $CurrentInput -Encoding utf8
# Edit the console line using VSCode
code --new-window --wait $tempFilePath
# Uncomment for using gvim editor instead
# gvim -f $tempFilePath
# The console doesn't like the CR character, so rejoin lines using LF only.
$editedInput = ((Get-Content -LiteralPath $tempFilePath) -join "`n").Trim()
# Replace current console line with the content of the temp file
[Microsoft.PowerShell.PSConsoleReadLine]::Replace(0, $currentInput.Length, $editedInput)
Remove-Item $tempFilePath
}
更新:
此 GitHub Gist 包括代码的更新版本。新功能包括 saving/restoring 光标位置、将光标位置传递给 VSCode 以及在控制台被阻止时显示消息。
备注:
- VSCode 的默认安装将 VSCode 二进制文件的目录添加到
$env:PATH
,这使我们能够只写 code
来启动编辑器。
- 虽然 UTF-8 是 PowerShell Core 上
Set-Content
等 cmdlet 的默认编码,但对于 Windows PowerShell 参数 -Encoding utf8
是正确保存包含 Unicode 字符的命令所必需的。对于 Get-Content
,不需要指定编码,因为 Windows PowerShell 添加了一个 BOM,Get-Content
检测到并且 PowerShell Core 再次默认为 UTF-8。
- 要让 Alt+E 在您打开控制台时始终可用,只需将此代码添加到您的
$profile
file。轻而易举地快速测试和编辑小代码示例。
- VSCode 设置 - 为了获得最流畅的体验,请启用“自动保存:onWindowChange”。允许您通过单击 Ctrl+W.
来关闭编辑器并保存文件(更新控制台行)
tl;dr
PSReadLine 附带您正在寻找的功能,即 ViEditVisually
功能,在 Unix-like 平台上它甚至有一个默认的键绑定。
要使该功能起作用,您需要将 $env:VISUAL
或 $env:EDITOR
设置为编辑器可执行文件的名称/路径。
从 PSReadLine v2.1 开始,如果您还需要为您的编辑器包含 选项 - 例如 -f
用于 gvim
- 你需要创建一个 helper 可执行文件,它有选项“baked in”。
由于创建辅助可执行文件很麻烦,自定义模拟该功能,正如您在 zett42's helpful answer 中尝试和改进的那样,目前可能是更简单的解决方案。 zett42 的答案还链接到一个要点,该要点通过保留光标位置来改进原始功能。
GitHub issue #3214 建议在这些环境变量中添加识别可执行文件 及其选项 的支持。
详情如下。
PSReadLine module ships with such a feature, namely the ViEditVisually
函数.
它的默认键绑定,如果有的话,取决于PSReadLine的edit mode,你可以用Set-PSReadLineOption -EditMode <mode>
设置:
Windows
模式(默认为 Windows):未绑定
Emacs
模式(macOS 和 Linux 上的默认模式):Ctrl-xCtrl-e
Vi
模式:v命令模式(按Esc进入)
使用Set-PSReadLineKeyHandler
建立自定义键绑定,类似于问题中的方法;例如,绑定 Alt-e:
Set-PSReadLineKeyHandler -Chord Alt+e -Function ViEditVisually
要使该功能正常工作,您必须通过以下任一环境变量定义要使用的编辑器可执行文件,按优先顺序排列:$env:VISUAL
或 $env:EDITOR
;如果未定义(有效的)编辑器,则会发出警告蜂鸣声,并且在调用该函数时不采取任何操作。
例如,要在 macOS 上使用 nano
编辑器:$env:VISUAL = 'nano'
值必须引用 可执行文件 - 通过完整路径或更常见的名称仅,在这种情况下,它必须位于 $env:PATH
中列出的目录中
- 注意:
.ps1
脚本不 符合可执行文件的条件,但批处理文件 Windows 和shebang-line-based shell Unix-like 平台上的脚本 可以。
自 PSReadLine 2.1 起,包括可执行文件的 选项 - 例如 --newindow --wait
用于 code
( Visual Studio 代码) 不 支持。
因此,现在,如果您选择的编辑器需要选项,您需要创建一个辅助可执行文件 具有这些选项“内置”;请参阅下面的示例。
GitHub issue #3214 提议添加支持以允许指定编辑器可执行文件 plus options 作为 environment-variable 值,这是Git(识别相同的变量)已经支持;例如,您可以定义:
$env:VISUAL = 'code --new-window --wait'
用于 code
(Visual Studio 代码)的辅助可执行文件 的示例配置:
在此示例中,在用户的主目录中创建一个辅助可执行文件:
在Windows:
'@code --new-window --wait %*' > "$HOME\codewait.cmd"
在 Unix-like 平台上:
"#!/bin/sh`ncode --new-window --wait `"$@`"" > "$HOME/codewait"; chmod a+x "$HOME/codewait"
将指向辅助可执行文件的 $env:VISUAL
的定义添加到您的 $PROFILE
文件,如果需要,还可以定义自定义键绑定:
$env:VISUAL = "$HOME/codewait"
# Custom key binding
Set-PSReadLineKeyHandler -Chord Alt+e -Function ViEditVisually
tl;博士
我想找到 bash
edit-and-execute-command
小部件或 zsh
edit-command-line
小部件的 Powershell 版本。
背景
短命令直接在命令行上执行,长而复杂的命令从脚本中执行。但是,在它们变得“长”之前,能够在命令行上测试中等长度的命令会有所帮助。为了协助完成这项工作,在外部编辑器中编辑命令变得非常有用。 AFAIK Powershell 本身不支持这个,例如bash
和 zsh
可以。
我目前的尝试
我是 Powershell 的新手,所以我肯定会犯很多错误,但我已经使用 [Microsoft.Powershell.PSConsoleReadLine]
的功能提出了一个可行的解决方案class。我可以将当前命令行复制到文件,编辑文件,然后将编辑后的版本重新注入命令行:
Set-PSReadLineKeyHandler -Chord "Alt+e" -ScriptBlock {
$CurrentInput = $null
# Copy current command-line input, save it to a file and clear it
[Microsoft.PowerShell.PSConsoleReadLine]::GetBufferState([ref] $CurrentInput, [ref] $null)
Set-Content -Path "C:\Temp\ps_${PID}.txt" -Value "$CurrentInput"
[Microsoft.PowerShell.PSConsoleReadLine]::KillRegion()
# Edit the command with gvim
Start-Job -Name EditCMD -ScriptBlock { gvim "C:\Temp\ps_${Using:PID}.txt" }
Wait-Job -Name EditCMD
# Get command back from file the temporary file and insert it into the command-line
$NewInput = (Get-Content -Path "C:\Temp\ps_${PID}.txt") -join "`n"
[Microsoft.PowerShell.PSConsoleReadLine]::Insert($NewInput)
}
问题
我目前的解决方案感觉笨拙而且有些脆弱。 还有其他解决方案吗?目前的解决方案可以改进吗?
环境
- OS Windows 10.0.19043.0
- Powershell 版本 5.1.19041.1320
- PSReadLine 版本 2.0.0
我采用的解决方案
创建类似于 mklement0 所示的“烘焙”可执行文件。我更喜欢 vim
而不是 `gvim,因为它直接在控制台中运行:
'@vim -f %*' > psvim.cmd
$env:EDITOR = "psvim"
Set-PSReadLineKeyHandler -Chord "Alt+e" -Function ViEditVisually
这段代码有一些问题:
- 临时路径是硬编码的,它应该使用
$env:temp
或更好的[IO.Path]::GetTempPath()
(为了 cross-platform 兼容性)。 - 编辑行后,不会替换整行,只替换光标左侧的文本。如 mklement0, we can simply replace 所述,现有缓冲区而不是擦除它,这解决了问题。
- 当为编辑器使用正确的参数时,不需要创建作业来等待它。对于 VSCode,这是
--wait
(-w
) and for gvim this is--nofork
(-f
),它可以防止这些进程与控制台进程分离,因此 PowerShell 代码会一直等到用户关闭编辑器。 - 关闭编辑器后临时文件没有被删除
这是我修复代码的尝试。我不用gvim
,所以用VSCodecode.exe
测试了一下。下面的代码也包含 gvim
的注释行(已由 OP 确认工作)。
Set-PSReadLineKeyHandler -Chord "Alt+e" -ScriptBlock {
$CurrentInput = $null
# Copy current console line
[Microsoft.PowerShell.PSConsoleReadLine]::GetBufferState([ref] $CurrentInput, [ref] $null)
# Save current console line to temp file
$tempFilePath = Join-Path ([IO.Path]::GetTempPath()) "ps_$PID.ps1"
Set-Content $tempFilePath -Value $CurrentInput -Encoding utf8
# Edit the console line using VSCode
code --new-window --wait $tempFilePath
# Uncomment for using gvim editor instead
# gvim -f $tempFilePath
# The console doesn't like the CR character, so rejoin lines using LF only.
$editedInput = ((Get-Content -LiteralPath $tempFilePath) -join "`n").Trim()
# Replace current console line with the content of the temp file
[Microsoft.PowerShell.PSConsoleReadLine]::Replace(0, $currentInput.Length, $editedInput)
Remove-Item $tempFilePath
}
更新:
此 GitHub Gist 包括代码的更新版本。新功能包括 saving/restoring 光标位置、将光标位置传递给 VSCode 以及在控制台被阻止时显示消息。
备注:
- VSCode 的默认安装将 VSCode 二进制文件的目录添加到
$env:PATH
,这使我们能够只写code
来启动编辑器。 - 虽然 UTF-8 是 PowerShell Core 上
Set-Content
等 cmdlet 的默认编码,但对于 Windows PowerShell 参数-Encoding utf8
是正确保存包含 Unicode 字符的命令所必需的。对于Get-Content
,不需要指定编码,因为 Windows PowerShell 添加了一个 BOM,Get-Content
检测到并且 PowerShell Core 再次默认为 UTF-8。 - 要让 Alt+E 在您打开控制台时始终可用,只需将此代码添加到您的
$profile
file。轻而易举地快速测试和编辑小代码示例。 - VSCode 设置 - 为了获得最流畅的体验,请启用“自动保存:onWindowChange”。允许您通过单击 Ctrl+W. 来关闭编辑器并保存文件(更新控制台行)
tl;dr
PSReadLine 附带您正在寻找的功能,即
ViEditVisually
功能,在 Unix-like 平台上它甚至有一个默认的键绑定。要使该功能起作用,您需要将
$env:VISUAL
或$env:EDITOR
设置为编辑器可执行文件的名称/路径。从 PSReadLine v2.1 开始,如果您还需要为您的编辑器包含 选项 - 例如
-f
用于gvim
- 你需要创建一个 helper 可执行文件,它有选项“baked in”。由于创建辅助可执行文件很麻烦,自定义模拟该功能,正如您在 zett42's helpful answer 中尝试和改进的那样,目前可能是更简单的解决方案。 zett42 的答案还链接到一个要点,该要点通过保留光标位置来改进原始功能。
GitHub issue #3214 建议在这些环境变量中添加识别可执行文件 及其选项 的支持。
详情如下。
PSReadLine module ships with such a feature, namely the
ViEditVisually
函数.它的默认键绑定,如果有的话,取决于PSReadLine的edit mode,你可以用
Set-PSReadLineOption -EditMode <mode>
设置:Windows
模式(默认为 Windows):未绑定Emacs
模式(macOS 和 Linux 上的默认模式):Ctrl-xCtrl-eVi
模式:v命令模式(按Esc进入)
使用
Set-PSReadLineKeyHandler
建立自定义键绑定,类似于问题中的方法;例如,绑定 Alt-e:Set-PSReadLineKeyHandler -Chord Alt+e -Function ViEditVisually
要使该功能正常工作,您必须通过以下任一环境变量定义要使用的编辑器可执行文件,按优先顺序排列:
$env:VISUAL
或$env:EDITOR
;如果未定义(有效的)编辑器,则会发出警告蜂鸣声,并且在调用该函数时不采取任何操作。例如,要在 macOS 上使用
nano
编辑器:$env:VISUAL = 'nano'
值必须引用 可执行文件 - 通过完整路径或更常见的名称仅,在这种情况下,它必须位于
中列出的目录中$env:PATH
- 注意:
.ps1
脚本不 符合可执行文件的条件,但批处理文件 Windows 和shebang-line-based shell Unix-like 平台上的脚本 可以。
- 注意:
自 PSReadLine 2.1 起,包括可执行文件的 选项 - 例如
--newindow --wait
用于code
( Visual Studio 代码) 不 支持。因此,现在,如果您选择的编辑器需要选项,您需要创建一个辅助可执行文件 具有这些选项“内置”;请参阅下面的示例。
GitHub issue #3214 提议添加支持以允许指定编辑器可执行文件 plus options 作为 environment-variable 值,这是Git(识别相同的变量)已经支持;例如,您可以定义:
$env:VISUAL = 'code --new-window --wait'
用于 code
(Visual Studio 代码)的辅助可执行文件 的示例配置:
在此示例中,在用户的主目录中创建一个辅助可执行文件:
在Windows:
'@code --new-window --wait %*' > "$HOME\codewait.cmd"
在 Unix-like 平台上:
"#!/bin/sh`ncode --new-window --wait `"$@`"" > "$HOME/codewait"; chmod a+x "$HOME/codewait"
将指向辅助可执行文件的
$env:VISUAL
的定义添加到您的$PROFILE
文件,如果需要,还可以定义自定义键绑定:
$env:VISUAL = "$HOME/codewait"
# Custom key binding
Set-PSReadLineKeyHandler -Chord Alt+e -Function ViEditVisually