Проблемы с поиском и заменой строк в PowerShell - PullRequest
0 голосов
/ 29 сентября 2018


Я довольно новичок в 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.Как я могу исправить эту программу, чтобы она вела себя так, как я хочу (как описано выше)?

Спасибо,
Гейб

Ответы [ 3 ]

0 голосов
/ 29 сентября 2018

Вы также можете сделать это с помощью оператора 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
0 голосов
/ 30 сентября 2018

Что касается вопроса № 2 ( Как я могу исправить эту программу [...]? ):

Полезный ответ Кирилла Пашкова предлагает элегантное решение на основев операторе 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

Вышеприведенное опирается на регулярных выражений (обычныйвыражения) для преобразования ввода;подробности о том, как результаты сопоставления регулярных выражений можно ссылать в операнде строки замены оператора -replace, см. этот ответ .

Предупреждение : существует множество другихсинтаксические различия между VBScript и JScript, которые не охватываются вашим подходом, в частности то, что VBScript не имеет оператора return и вместо этого использует <funcName> = ... для возврата значений из функций.


Что касается вопроса № 1:

Однако, когда я открываю outfile.js, я вижу только одну строку:
False
[...]
1. Почему это происходит?

  • Все, кроме вызова first ForEach-Object командлета, выполняются в отдельных операторах , потому чтоначальный конвейер заканчивается с первым вызовом Out-File $js.

  • последующими ForEach-Object вызовами каждого началановый конвейер , и так как каждый конвейер заканчивается Out-File $js, каждый , такой конвейер записывает в файл $js - и тем самым перезаписываетes независимо от того, что написал предыдущий.
    Следовательно, последний конвейер определяет конечное содержимое файла $js.

  • AForEach-Object что запускает конвейер получает нет ввода .Однако связанный с ним блок скрипта ({...}) все еще вводится один раз в этом случае, с $_, равным $null [1] :

    • Последний конвейер начинается с Foreach-Object { $_ -match "End Function" }, поэтому его вывод эквивалентен $null -match "End Function", что дает $False, потому что -match с скаляром LHS (один входной объект) выводит логическое значение, которое указывает, было ли найдено совпадение.

    • Следовательно, учитывая, что средний сегмент конвейера (Foreach-Object { $_ -replace "End Function", "}" }) действует no-op ($False переводится в 'False', и поэтому оператор -replace не находит соответствия для замены и пропускает строковый ввод без изменений), Out-File $js получаетстрока 'False' и записывает именно это в выходной файл $js.


Даже если вы преобразовали свои отдельные команды в single конвейер с одним Out-File $js сегментом в самом конце, ваша команда не будет работать, однако:

Учитывая, что Get-Content отправляет строки входного файла по конвейеру одна за другой , что-то вроде $_ -match "Sub " снова выдаст Boolean результат - указывающий, соответствует ли соответствующая строка ($_)строка "Sub " - и передать , что включено.

Хотя вы можете превратить -match в фильтр , сделав LHS массивом - заключив его в оператор подвыражения массива @(...);например, @($_) -match "Sub " - это будет:

  • проходить строку, содержащую подстроку Sub - в целом , и
  • пропустить строки, которые не.

Другими словами: это не будет работать так, как задумано, потому что:

  • строк, которые не содержат совпадающую подстроку, будут опущены из выходных данных и
  • совпадающие строки отражаются полностью в $_ в следующем сегменте конвейера, а не только в согласованной части .

[1] Строго говоря, $_ сохранит любое значение, которое оно имело в текущей области, но это будет не $null, если вы явно присвоили значение $_ - учитывая, что $_ - это автоматическая переменная, которая обычно контролируется самой PowerShell, однако делать это не рекомендуется - см. это обсуждение GitHub .

0 голосов
/ 29 сентября 2018

ОК, в этом сценарии есть несколько ошибок. 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"

Это не учитывает добавление {в начале функции или добавление эфира (),Надеемся, что благодаря предоставленной информации вы на правильном пути.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...