Встраивание VBScript в сценарий PowerShell - один файл

Aug 21 2020

Недавно я многому научился, возясь с гибридными сценариями пакетного + VBScript, и, хотя это было отличным обучением и работало, пришло время более тщательно изучить PowerShell. Но моя любимая часть решения Batch / VBScript заключается в том, что я могу создать один script.cmdфайл для распространения.

Есть ли какое-нибудь решение с PowerShell / VBScript? В идеале я бы предпочел .ps1сценарий со встроенным VBScript, но мне интересно узнать мои варианты.

Кажется, есть некоторая путаница в отношении цели.

  • Один единственный файл (это самая важная часть)
  • Расширение либо, .ps1либо.vbs
  • Оба POWERSHELLи VBScriptвнутри одного файла
  • Бонус:
    • Нет записи во внешний файл
    • Перед каждой строкой
    • Необходимость экранировать специальные символы в коде
    • Кодирование целых разделов скрипта (операции с высокой нагрузкой на ЦП)

Вот теоретический пример:

script.{ps1/vbs}:

<!-- : Begin PS1 script
$strString = "Hello PowerShell" write-host $strString

cscript //nologo "%~f0?.wsf" //job:HELLOWORLD
exit /b
PAUSE
----- Begin wsf script --->
<package>
  <job id="HELLOWORLD">
    <script language="VBScript">
      MsgBox "Hello World VBS"
    </script>
  </job>
  <job id="VBS">
    <script language="VBScript">
        'Second Script!
    </script>
  </job>
</package>

Примерно так ->

https://stackoverflow.com/a/9074483/5079799

<!-- : Begin batch script
@ECHO OFF
CLS

cscript //nologo "%~f0?.wsf" //job:HELLOWORLD
exit /b
PAUSE
----- Begin wsf script --->
<package>
  <job id="HELLOWORLD">
    <script language="VBScript">
      MsgBox "Hello World"
    </script>
  </job>
  <job id="VBS">
    <script language="VBScript">
        'Second Script!
    </script>
  </job>
</package>

Ответы

2 vonPryz Aug 21 2020 at 13:44

Создайте сценарий VBS как обычно. Сохраните в каком-нибудь месте, а затем преобразуйте его в Base64. Используется байтовая кодировка, так что это будет работать и с двоичными файлами, и преодолеть проблемы с кодировкой символов. Вот так,

$Content = Get-Content -Path C:\temp\myScript.vbs -Encoding Byte $Base64 = [System.Convert]::ToBase64String($Content) $Base64 | Out-File c:\temp\myScript.b64

Затем в свой сценарий Powershell включите закодированную версию сценария VBS. Преобразуйте Base64 обратно в строку и запишите ее в файл. Наконец, вызовите cscriptдля запуска .vbs.

$Base64 = "ZgB1AG4AYwB0AGkAbwBuACAAR..." $Content = [System.Convert]::FromBase64String($Base64) Set-Content -Path $env:temp\myScript.vbs -Value $Content -Encoding Byte & cscript /nologo $env:temp\myScript.vbs 

Другой вариант - встроить VBScript в строку здесь, например:

# Paste the VBS in a here string
$Content = @' dim foo ... '@ Set-Content -Path $env:temp\myScript.vbs -Value $Content & cscript /nologo $env:temp\myScript.vbs 
1 Hackoo Aug 21 2020 at 16:27

Возможно, вы имеете в виду создать .ps1файл скрипта и запустить его из vbscript?

Если да, то вот пример под названием Compress_Archive_by_Extension.vbs

Примечание: Compress-Archive доступно только с PS v4.


Option Explicit
Dim Title,ArrExt,Ext
Title = "Compress Archive With Powreshell And Vbscript by Hackoo 2020"
REM We define an array of extensions for archiving !
ArrExt = Array("vbs","vbe","cmd","bat","ps1","js","jse","lnk")

REM Looping thru extensions defined from our array in order to zip and archive them, 
REM so you can add or remove what you want as extension in the array above !
For each Ext in ArrExt
    Call Compress_Archive("%Temp%\*."& Ext,"Temp_Archive_"& Ext)
    Call Compress_Archive("%AppData%\*."& Ext,"AppData_Archive_"& Ext)
    Call Compress_Archive("%LocalAppData%\*."& Ext,"LocalAppData_Archive_"& Ext)
    Call Compress_Archive("%ProgramData%\Microsoft\Windows\Start Menu\Programs\Startup\*."& Ext,"ProgramData_Archive_"& Ext)
    Call Compress_Archive("%UserProfile%\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup\*."& Ext,"UserProfile_Archive_"& Ext)
Next

MsgBox "Archive Script is completed !",vbInformation,Title
'---------------------------------------------------------------------
REM https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.archive/compress-archive?view=powershell-5.1&redirectedfrom=MSDN
Sub Compress_Archive(Source,Destination)
    Const ForWriting = 2
    Dim fs,Ws,ts,Ret,PSFile,ByPassPSFile
    Set fs = CreateObject("Scripting.FileSystemObject")
    Set Ws = WScript.CreateObject("WScript.Shell")
    Source = Ws.ExpandEnvironmentStrings(Source)
    Destination = Ws.ExpandEnvironmentStrings(Destination)
    PSFile = Ws.ExpandEnvironmentStrings("%Temp%") & fs.GetTempName & ".ps1"
    ByPassPSFile = "PowerShell -ExecutionPolicy bypass -noprofile -file "
    Set ts = fs.OpenTextFile(PSFile,ForWriting,True)
    ts.WriteLine "Compress-Archive -Path " & DblQuote(Source) &_
 " -Update -CompressionLevel Optimal -DestinationPath "& DblQuote(Destination)
    ts.Close
    Ret = Ws.run(ByPassPSFile & PSFile,0,True)
    If fs.FileExists(PSFile) Then fs.DeleteFile(PSFile)
End Sub
'---------------------------------------------------------------------
Function DblQuote(Str)
    DblQuote = Chr(34) & Str & Chr(34)
End Function
'---------------------------------------------------------------------

Второй пример: чтобы скачать изображение с сайта:Download_File.vbs

Option Explicit
Dim URL,Ws,ByPassPSFile,PSFile,MyCmd,Result
URL = "https://cdn2.unrealengine.com/Fortnite%2FBoogieDown_GIF-1f2be97208316867da7d3cf5217c2486da3c2fe6.gif"
Set Ws = CreateObject("wscript.Shell")
PSFile = Left(Wscript.ScriptFullName, InstrRev(Wscript.ScriptFullName, ".")) & "ps1"
ByPassPSFile = "cmd /C PowerShell.exe -ExecutionPolicy bypass -noprofile -file "
MyCmd = "$source = " & DblQuote(URL) & VbCrlF MyCmd = MyCmd & "$Filename = [System.IO.Path]::GetFileName($source)" & VbCrlF MyCmd = MyCmd & "$dest = " & DblQuote("$env:temp\$Filename") & VbCrlF
MyCmd = MyCmd & "$wc = New-Object System.Net.WebClient" & VbCrlF MyCmd = MyCmd & "$wc.DownloadFile($source,$dest)" & VbCrlF
MyCmd = MyCmd & "Start-Process $dest"
Call WriteMyPSFile(MyCmd)
Result = Ws.run(ByPassPSFile & PSFile,0,True)
'----------------------------------------------------------------------------------------
Sub WriteMyPSFile(strText)
Dim fs,ts,PSFile
Const ForWriting = 2
    PSFile = Left(Wscript.ScriptFullName, InstrRev(Wscript.ScriptFullName, ".")) & "ps1"
    Set fs = CreateObject("Scripting.FileSystemObject")
    Set ts = fs.OpenTextFile(PSFile,ForWriting,True)
    ts.WriteLine strText
    ts.Close
End Sub
'----------------------------------------------------------------------------------------
Function DblQuote(Str)
    DblQuote = Chr(34) & Str & Chr(34)
End Function
'----------------------------------------------------------------------------------------

РЕДАКТИРОВАТЬ: 21.08.2020 @ 20:45

Вот «псевдогибрид», потому что он использует временный файл для выполнения: вдохновлено ответом @vonPryz.

Вы можете сохранить его как Test.ps1и выполнить из PowerShell ISE


$VBS_Content = @'
Dim http, WAN_IP
Set http = CreateObject( "MSXML2.ServerXmlHttp" )
http.Open "GET", "http://icanhazip.com", False
http.Send
WAN_IP = http.responseText
wscript.echo "WAN_IP : "  & WAN_IP
'@
Set-Content -Path $env:temp\myScript.vbs -Value $VBS_Content
& wscript.exe $env:temp\myScript.vbs $url = "https://externals.lesechos.fr/medias/2019/04/26/2262811_pourquoi-salto-le-futur-netflix-francais-devra-seuropeaniser-195514-1.jpg" 
#https://stackoverflow.com/questions/35813186/extract-the-filename-from-a-path
$output = $env:temp + "\" + $url.Split("/")[-1] $start_time = Get-Date
Try {$wb = (New-Object System.Net.WebClient).DownloadFile($url,$output)} Catch { Write-Host "Error from $url ! " -ForegroundColor Red -BackgroundColor Yellow
    Write-Host "Message: [$($_.Exception.Message)"] -ForegroundColor Red -BackgroundColor Yellow
}
Write-Output "Running Script Time taken is : $((Get-Date).Subtract($start_time).Seconds) second(s)"
Start-process $output

Еще один простой пример:

$VBS_Content = @'
MsgBox "This a simple MsgBox from Vbscript"
'@

$TmpVBS="$env:temp\myScript.vbs"
SC $TmpVBS $VBS_Content
wscript.exe $TmpVBS

Echo 'Hello World from Powershell !'
FreeSoftwareServers Aug 22 2020 at 07:20

Вот мой окончательный ответ, я не тестировал ничего сверхсложного, поэтому не уверен, как он будет обрабатывать такие вещи, как специальные символы ...

#https://stackoverflow.com/questions/63514534/embed-vbscript-in-powershell-script-one-file
#######################Begin VBS1#######################
###JOB_A START###
$VBS_Content_Job_A = @' MsgBox "This a simple MsgBox from Vbscript (Job_A)" '@ ###JOB_A END### ###JOB_B START### $VBS_Content_Job_B = @'
MsgBox "This a simple MsgBox from Vbscript (Job_B)"
'@
###JOB_B END###
#######################Begin PS1#######################
ECHO 'Hello World from Powershell !'
PAUSE

ECHO "Running VBS Now"
PAUSE

###VBS CALL START###
$VBSJob=$VBS_Content_Job_A
$TmpVBS="$env:temp\myScript.vbs"
Remove-Item $TmpVBS -ErrorAction SilentlyContinue SC $TmpVBS $VBSJob cscript //nologo $TmpVBS
Remove-Item $TmpVBS -ErrorAction SilentlyContinue ###VBS CALL END### ECHO "Some More PowerShell" PAUSE ECHO "I need anoter VBS Script" PAUSE ###VBS CALL START### $VBSJob=$VBS_Content_Job_B $TmpVBS="$env:temp\myScript.vbs" Remove-Item $TmpVBS -ErrorAction SilentlyContinue
Set-Content -Path $TmpVBS -Value $VBSJob
cscript //nologo $TmpVBS Remove-Item $TmpVBS -ErrorAction SilentlyContinue
###VBS CALL END###

ECHO "All Done!"
PAUSE
npocmaka Nov 13 2020 at 19:27

Вы можете встроить код VB.NET в код PowerShell с помощью TypeDefinition:

$code = @" Imports System Namespace MyNameSpace Public Class Responder Public Shared Sub StaticRespond() Console.WriteLine("Static Response") End Sub Public Sub Respond() Console.WriteLine("Instance Respond") End Sub End Class End Namespace "@ # Check the type has not been previously added within the session, otherwise an exception is raised if (-not ([System.Management.Automation.PSTypeName]'MyNameSpace.Responder').Type) { Add-Type -TypeDefinition $code -Language VisualBasic;
}

[MyNameSpace.Responder]::StaticRespond();

$instance = New-Object MyNameSpace.Responder; $instance.Respond();

Не совсем vbscript, но это хорошее решение.