【问题标题】:Pipe stream to other process管道流到其他进程
【发布时间】:2021-09-28 14:59:41
【问题描述】:

我想下载一个 20GB 的转储文件,替换字符串并将其通过管道传输到 mysql.exe,同时它仍在 Powershell 中下载。但我在传输流时遇到问题。

如果我的文件已经下载,我可以在将文件中的字符串替换为 StdOut 的同时进行流式传输:

Get-Content 'dump.sql' | %{ $_.replace("production_db", "staging_db") }

或者,如果我在流式传输并将字符串替换到 StdOut 时也下载文件,我可以这样做:

$url = 'http://MyServer.ext/dump.sql'
& {
    $myHttpWebRequest = [System.Net.WebRequest]::Create($url)
    $myHttpWebRequest.Headers.Add("Authorization", "Basic " + [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes("MyUsername:MyPassword")))
    try {
        $res = $myHttpWebRequest.GetResponse()
    }
    catch [System.Net.WebException] {
        $res = $_.Exception.Response
    } 
    if ([int] $res.StatusCode -ne 200) {
        'Error: ' + [int]$res.StatusCode + " " + $res.StatusCode
    } else {
        $receiveStream = $res.GetResponseStream()
        $encode = [System.Text.Encoding]::GetEncoding("utf-8")
        $readStream = [System.IO.StreamReader]::new($receiveStream, $encode)
        while (-not $readStream.EndOfStream) {
            $readStream.ReadLine().replace("production_db", "staging_db")
        }
        $res.Close()
        $readStream.Close()
    }
}

但在这两种情况下,我都无法将其作为流传输到 mysql.exe。当我追加时,它将整个流首先加载到内存中,然后再传递给 mysql.exe 进程:

 | & 'C:\Program Files\MySQL\MySQL Server 5.7\bin\mysql.exe' -u MyUsername -pMyPassword -h 127.0.0.1

如何在 Powershell 中将流通过管道传输到另一个进程?

【问题讨论】:

  • Afaik,外部命令对于 PowerShell 管道是未知的,这意味着您需要将每一行(每个 完整的 sql 命令)分别流式传输到 mysql.exe:... |ForEach-Object { & 'C:\Program Files\MySQL\MySQL Server 5.7\bin\mysql.exe' -u MyUsername -pMyPassword -h 127.0.0.1 }
  • @iRon 恐怕这无济于事。因为为每个语句运行另一个 mysql.exe 会很慢。还有一些语句甚至相互依赖(比如设置环境变量)。
  • @nl-x 作为一种解决方法,您可以尝试使用type dump.sql | mysql.exe 命令创建一个.bat 文件并使用PowerShell 脚本中的适当参数执行此.bat 文件
  • 我认为最好的解决方案是删除外部命令 mysql.exe 并使用数据库连接器继续使用 .Net 和 com 对象。参见例如:Which driver should I install so that mysqlcommand can be run using powershell?

标签: powershell stream pipe


【解决方案1】:

我们知道 mysql.exe 将采用标准输入,因为我们可以运行 mysql.exe < myfile.sql 并且效果很好。我找到了this关于将文本行发送到进程的标准输入的绝妙答案,它似乎对我有用。我正在使用 CMD shell 进行测试,因为我手边没有 mysql:

# Setup: example commands to feed via stream to process
'echo "hello"
echo "world"
echo "bye"
exit' > 'C:\temp\file.bat'

# create streamreader from file
$readStream = [System.IO.Streamreader]::new('C:\temp\file.bat')
$encode = [System.Text.Encoding]::GetEncoding("utf-8")

对你来说,只需更新你的替换和 exe 名称:

# Create a process for the job
$psi = New-Object System.Diagnostics.ProcessStartInfo;
$psi.FileName = "cmd.exe"; #process file
$psi.UseShellExecute = $false; #start the process from it's own executable file
$psi.RedirectStandardInput = $true; #enable the process to read from standard input
$psi.RedirectStandardOutput = $true #send standard output to object

$p = [System.Diagnostics.Process]::Start($psi);

# Send each line of the file to the process as standard input:
while (-not $readStream.EndOfStream) {
  $p.StandardInput.WriteLine(
    ($readStream.ReadLine().replace("l", "L"))
  )
}

$readStream.Close()

# Make sure you don't accidentally close mysql before it's done processing.
# For example, add WriteLine('exit') after the While{}, then use Wait-Process.
# get the output of the process, should also wait for the process to finish
$p.StandardOutput.ReadToEnd()
$p.Close() 

在我的测试中,它正在工作,在读取命令时替换它们,并将它们发送到进程,并显示输出:

Microsoft Windows [Version 10.0.19043.1110]
(c) Microsoft Corporation. All rights reserved.

C:\>echo "heLLo"
"heLLo"

C:\>echo "worLd"
"worLd"

C:\>echo "bye"
"bye"

C:\>exit

我认为您应该能够挂接到 $p.StandardOutput 并随时读取它,但是当我尝试它时会导致进程挂起?也许只是使用 mysql 日志记录。

【讨论】:

    猜你喜欢
    • 2012-05-10
    • 2013-03-28
    • 2013-06-22
    • 2021-03-27
    • 1970-01-01
    • 2012-06-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多