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

iswrvxsc  于 2022-11-11  发布在  Shell
关注(0)|答案(1)|浏览(121)

我正在尝试创建一个Powershell函数,作为WSL中Vim的 Package 器。我希望这个 Package 器能够接受来自管道的输入。
在WSL中,可以执行以下操作:

$ cat manylines.txt | vim -

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

Get-Content manylines.txt | vim -

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

Get-Content manylines.txt | powershell -Command vim -
Get-Content manylines.txt | cmd /c vim -

但是,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 -

在前两种情况下,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 -"

但当$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 -"

如何让WSL Vim接受来自Powershell的管道输入?
其他注意事项:

  • 我可以使用一个临时文件并将该文件传递给WSLVim,但这不是一个很好的解决方案,而且会留下一个必须清理的文件。
eoxn13cs

eoxn13cs1#

事实上,通过wslvim输入的 * 管道 * 似乎破坏了vim的键盘接口的 * 光标移动 *(例如,我仍然能够键入:q!退出)。
解决方法是改进您发现的方法,即提供输入 * 作为传递 * 给wsl的shell命令的一部分,而不是使用管道:
这样做需要小心转义和转换,如以下 Package 函数所示:


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

注意事项:

  • 当传递命令输出时,您可能会遇到 max. command-line length
  • 在传递文件路径时,相对文件路径可以正常工作,但绝对路径仅适用于 * 本地 * 驱动器(Map驱动器/ UNC路径需要首先在WSL端装载卷)。

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

vim + somefile.txt

Get-ChildItem | vim +

相关问题