【问题标题】:Issues finding and replacing strings in PowerShell在 PowerShell 中查找和替换字符串的问题
【发布时间】:2019-03-05 08:04:41
【问题描述】:


我对 PowerShell 比较陌生,我正在尝试编写一个 PowerShell 脚本来将 VBScript 中的一些语句转换为 Microsoft JScript。这是我的代码:

$vbs = 'C:\infile.vbs'
$js = 'C:\outfile.js'

(Get-Content $vbs | Set-Content $js)
(Get-Content $js) |
 Foreach-Object { $_ -match "Sub " } | Foreach-Object { "$_()`n`{" } | Foreach-Object { $_ -replace "Sub", "function" } | Out-File $js
 Foreach-Object { $_ -match "End Sub" } | Foreach-Object { $_ -replace "End Sub", "`}" } | Out-File $js
 Foreach-Object { $_ -match "Function " } | Foreach-Object { "$_()`n`{" } | Foreach-Object { $_ -replace "Function", "function" } | Out-File $js
 Foreach-Object { $_ -match "End Function" } | Foreach-Object { $_ -replace "End Function", "`}" } | Out-File $js

我想要让我的 PowerShell 程序从 VBScript 输入文件 infile.vbs 中获取代码,对其进行转换,然后将其输出到 JScript 输出文件 outfile.js。这是我想要它做的一个例子:

输入文件:

Sub HelloWorld
 (Code Here)
End Sub

输出文件:

function HelloWorld()
{
 (Code Here)
}

在函数方面也会发生类似的情况。从那里,我会手动调整代码以进行转换。当我在 PowerShell v5.1 中运行我的程序时,它没有显示任何错误。但是,当我打开outfile.js 时,我只看到一行:

False

真的,我有两个问题。
1。为什么会发生这种情况?
2。我怎样才能修复这个程序,让它按照我想要的方式运行(如上所述)?

谢谢,
加布

【问题讨论】:

    标签: regex powershell replace io


    【解决方案1】:

    您也可以使用switch 语句来执行此操作。像这样:

    $vbs = 'C:\infile.vbs'
    $js = 'C:\outfile.js'
    
    Get-Content $vbs | ForEach-Object {
            switch -Regex ($_) {
            'Sub '{
                'function {0}(){1}{2}' -f $_.Remove($_.IndexOf('Sub '),4).Trim(),[Environment]::NewLine,'{'
            }
            'End Sub'{
                '}'
            }
            'Function ' {
                'function {0}(){1}{2}' -f $_.Remove($_.IndexOf('Function '),9).Trim(),[Environment]::NewLine,'{'
            }
            'End Function' {
                '}'
            }
            default {
                $_
            }
        }
    } | Out-File $js
    

    【讨论】:

      【解决方案2】:

      至于问题 #2(我该如何修复这个程序 [...]?):

      Kirill Pashkov's helpful answer 提供了一个基于switch 语句的优雅解决方案。

      但请注意,他的解决方案:

      • 基于Sub <name> / Function <name> 语句部分与匹配的End Sub / End Function 部分不在同一行 - 虽然这是通常 的情况,这不是 句法上的要求;例如,Sub Foo() WScript.Echo("hi") End Sub - 在单行上 - 也可以。

      • 根据您自己的解决方案尝试,盲目地将() 附加到Sub / Function 定义,这不适用于已经具有参数声明的输入过程/函数(例如,Sub Foo (bar, baz) )。

      以下解决方案:

      • 也适用于单行 Sub / Function 定义
      • 正确保留参数声明
      Get-Content $vbs | ForEach-Object {
        $_ -replace '\b(?:sub|function)\s+(\w+)\s*(\(.*?\))', 'function $1$2 {' `
           -replace '\bend\s+(?:sub|function)\b', '}'
      } | Out-File $js
      

      上面严重依赖regexes (regular expressions)来转换输入;有关如何在-replace 运算符的替换字符串操作数中引用正则表达式匹配结果的详细信息,请参阅this answer

      警告:VBScript 和 JScript 之间还有许多其他语法差异,您的方法没有涵盖,特别是 VBScript 没有 return 语句,而是使用 <funcName> = ... 从函数返回值.


      关于第一个问题:

      但是,当我打开 outfile.js 时,我只看到一行:
      False
      [...]
      1. 为什么会这样?

      • 除了 first ForEach-Object cmdlet 调用之外的所有命令都在单独的语句中运行,因为初始管道结束第一次调用Out-File $js

      • 随后的ForEach-Object 调用每个启动一个新管道,并且由于每个管道以Out-File $js 结束,每个这样的管道写入文件@987654346 @ - 从而覆盖前一个写的内容。
        因此,last 管道决定了文件$js 的最终内容。

      • ForEach-Object启动管道接收没有输入。但是,在这种情况下,其关联的脚本块 ({...}) 仍会输入一次,其中$_$null[1]

        • 最后一个管道以Foreach-Object { $_ -match "End Function" } 开头,因此它的输出相当于$null -match "End Function",产生$False,因为-match 带有标量 LHS(单个input object) 输出一个 Boolean 值,指示是否找到匹配项。

        • 1234563并且-replace 运算符因此找不到要替换的匹配项并将字符串化的输入未经修改地传递出去),Out-File $js 接收字符串'False' 并将其写入输出文件$js

      即使您将单独的命令转换为 single 管道,最后只有一个 Out-File $js 段,但您的命令也不起作用:

      鉴于Get-Content 通过管道一个接一个 发送输入文件的行,类似$_ -match "Sub " 将再次产生一个布尔 结果 - 指示该行是否手头 ($_) 匹配字符串 "Sub " - 并传递 that

      虽然您可以通过将 LHS 设置为 array-match 转换为 filter - 将其包含在数组子表达式运算符 @(...) 中;例如,@($_) -match "Sub " - 那会:

      • 将包含子字符串Sub 的行通过作为一个整体,并且
      • 省略行。

      换句话说:这不会按预期工作,因为:

      • 不包含匹配子字符串的行将从输出中省略,并且
      • 匹配的行完整反映在下一个管道段的$_ 中 - 而不仅仅是匹配的部分

      [1] 严格来说,$_ 将保留它在当前范围内的任何值,但只有在您明确地将值分配给 $_ - 鉴于 $_ 是一个 自动 变量,通常由 PowerShell 本身控制,但是这样做是不明智的 - 请参阅 this GitHub discussion

      【讨论】:

        【解决方案3】:

        好的,这个脚本有一些问题。 Foreach-Object 也称为 % 是迭代管道中的每个项目。 例子是

        @(1..10) | %{ "This is Array Item $_"}
        

        这将输出 10 行计算数组项。在您当前的脚本中,您应该在 Where-Object 也称为 ? 的地方使用它。

        @(1..10) | ?{ $_ -gt 5 }
        

        这将输出所有大于 5 的数字。

        你想要追求的一个例子是这样的

        function ConvertTo-JS([string]$InputFilePath,[string]$SaveAs){
            Get-Content $InputFilePath |
                %{$_ -replace "Sub", "function"} |
                %{$_ -replace "End Function", "}"} |
                %{$_ -replace "Function", "function"} |
                %{$_ -replace "End Function", "}" } |
                Out-File $SaveAs
        }
        
        ConvertTo-JS -InputFilePath "C:\TEST\TEST.vbs" -SaveAs "C:\TEST\TEST.JS"
        

        这不考虑在函数开头添加 { 或添加 () 以太。但希望提供的信息能让您走上正轨。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2017-06-15
          • 2021-12-19
          • 2013-02-23
          • 2014-12-03
          • 2021-12-11
          • 1970-01-01
          • 2011-08-22
          相关资源
          最近更新 更多