在PowerShell中查找和替换字符串时出现问题

时间:2018-09-29 16:29:23

标签: regex powershell replace io


我对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。如何修复此程序,使其表现出我想要的效果(如上所述)?

谢谢,
Gabe

3 个答案:

答案 0 :(得分: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

答案 1 :(得分:0)

好的,此脚本有些错误。 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"

这不考虑在函数的开头添加{或添加()以太。但是随着所提供的信息有望使您走上正确的轨道。

答案 2 :(得分:0)

关于问题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之外的所有cmdlet调用均在单独的语句中运行,因为初始管道结束 first 调用Out-File $js

  • 随后的ForEach-Object调用每个开始一个新管道,并且由于每个管道都以Out-File $js结尾,因此每个这样的管道都会写入归档$js-从而覆盖上一个记录的内容。
    因此,确定文件$js最终内容的是 last 管道。

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

    • 最后一个管道以Foreach-Object { $_ -match "End Function" }开头,因此其输出与$null -match "End Function"等效,其结果为$False,因为-match的标量为 LHS(单个输入对象)输出一个 Boolean 值,该值指示是否找到了匹配项。

    • 因此,假设中间管道段(Foreach-Object { $_ -replace "End Function", "}" })是有效的 no-op $False字符串化为'False',因此-replace运算符找不到要替换的匹配项,并且将未修改的字符串化输入传递出去),Out-File $js接收字符串'False'并将其仅写入输出文件{{ 1}}。


即使您将单独的命令转换为最后只有一个$js段的单个管道,您的命令也无法使用,但是:

假设Out-File $js通过管道一个传送输入文件的行,类似Get-Content的东西将再次产生 Boolean 结果-指示当前行($_ -match "Sub ")是否与字符串$_相匹配-并传递那个

通过将LHS设置为 array ,可以将"Sub "变成 filter -将其封装在数组子表达式运算符-match中;例如@(...)-将:

  • 将包含子字符串@($_) -match "Sub "的行作为整体通过
  • 省略行。

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

    不包含匹配子字符串的
  • 行将从输出中被忽略,并且
  • 确实匹配的行会在下一个管道段的Sub中完整反映 ,而不仅仅是匹配部分

[1]严格来说,$_将保留其在当前范围内具有的任何值,但是如果您明确地将一个值分配给了$_ $null -假设$_是通常由PowerShell本身控制的 automatic 变量,但是这样做是不明智的-请参见{{3} }。