如何让 WSL Vim 接受来自 Powershell 的管道输入?

equ*_*all 5 vim powershell windows-subsystem-for-linux

我正在尝试创建一个 Powershell 函数,充当 WSL 中 Vim 的包装器。我希望这个包装器能够接受来自管道的输入。

在 WSL 中,可以执行以下操作:

$ cat manylines.txt | vim -
Run Code Online (Sandbox Code Playgroud)

指示-Vim 应该从 stdin 读取输入,然后通过管道重定向该输入。这将打开 vim,命令输出已在缓冲区中,但没有任何关联的文件。(您也可以简单地使用 打开文件vim manylines.txt,但这通常不适用于其他命令或可执行文件的输出)。

在 Powershell 和 Windows 原生 Vim 中也可以实现同样的效果:

Get-Content manylines.txt | vim -
Run Code Online (Sandbox Code Playgroud)

可以更进一步,在另一个 Powershell 或 cmd 实例中执行 Vim:

Get-Content manylines.txt | powershell -Command vim -
Get-Content manylines.txt | cmd /c vim -
Run Code Online (Sandbox Code Playgroud)

但是,这不适用于 WSL Vim。

# doesn't work, Vim freezes
Get-Content manylines.txt | wsl vim -
Get-Content manylines.txt | wsl bash -c "vim -"

# doesn't work, Vim quits reporting "Error reading Input"
Get-Content manylines.txt | wsl bash -c vim -
Run Code Online (Sandbox Code Playgroud)

在前两种情况下,Vim 正确打开,缓冲区中包含 Manylines.txt 的内容...然后拒绝接受任何键盘输入。无法使用 进行导航、编辑甚至退出:q!

使用临时 powershell 变量似乎可以解决问题,但仅当输入由单行组成时

# "works as long as the file only has a single line of text"
# "$tmpvar has type System.String (not an array)"
$tmpvar = Get-Content singleline.txt
wsl bash -c "echo $tmp | vim -"
Run Code Online (Sandbox Code Playgroud)

但是当 $tmpvar 变成 String[] 数组或带有任何换行符的 String 时,就会中断

# sort of works, but newlines are converted to a single space
$tmpvar = [string[]] Get-Content manylines.txt
wsl bash -c "echo $tmp | vim -"

# combines the strings in the array, inserts a newline between each pair of elements
$tmpvar = ([string[]] Get-Content manylines.txt) -join "`n"
# doesn't work, bash interprets the newline as the start of the next command
wsl bash -c "echo $tmp | vim -"
Run Code Online (Sandbox Code Playgroud)

如何让 WSL Vim 接受来自 Powershell 的管道输入?

其他注意事项:

  • 我可以使用临时文件并将该文件传递给 WSL Vim,但这不是一个优雅的解决方案,并且会留下一个必须清理的文件。除非临时文件在当前目录中,否则还涉及 wslpath 的额外恶作剧

mkl*_*nt0 4

事实上,管道输入到vimviawsl似乎破坏了光标移动的vim键盘界面(例如,我仍然能够输入退出)。:q!

解决方法是改进您发现的方法,即提供输入作为传递给的 shell 命令的一部分,wsl而不是使用管道:

这样做需要仔细的转义和转换,如以下包装函数所示:

# PowerShell wrapper function for invoking vim via WSL
function vim {
  if ($MyInvocation.ExpectingInput) { # pipeline input present
    $allInput = ($input | Out-String) -replace '\r?\n', "`n" -replace "'", "'\''" -replace '"', '\"'
    wsl -e sh -c "printf %s '$allInput' | vim $args -"
  }
  elseif ($args) { # no pipeline input, arguments that may include file paths given 
    wsl -e vim ($args.ToLower() -replace '^([a-z]):', '/mnt/$1' -replace '\\', '/')
  } else { # no pipeline input, no pass-through arguments
    wsl -e vim
  }
}
Run Code Online (Sandbox Code Playgroud)

注意事项:

  • 传递命令输出时,您可能会遇到最大值。命令行长度
  • 传递文件路径时,相对文件路径可以正常工作,但绝对文件路径仅适用于本地驱动器(映射驱动器/UNC 路径需要首先在 WSL 端安装卷)。

定义此函数后,您可以调用(+作为示例选项,告诉vim将光标放在输入的末尾):

vim + somefile.txt
Run Code Online (Sandbox Code Playgroud)

或者

Get-ChildItem | vim +
Run Code Online (Sandbox Code Playgroud)