Osadź VBScript w skrypcie PowerShell - jeden plik

Aug 21 2020

Niedawno nauczyłem się dużo majstrować przy hybrydowych skryptach wsadowych + VBScript i chociaż było to świetne narzędzie do nauki i działa, nadszedł czas, aby dokładniej nauczyć się PowerShell. Ale moją ulubioną częścią rozwiązania Batch / VBScript jest to, że mogę utworzyć pojedynczy script.cmdplik do dystrybucji.

Czy jest jakieś rozwiązanie z PowerShell / VBScript? Idealnie, myślę, że wolałbym .ps1skrypt z osadzonym VBScript, ale chciałbym poznać moje opcje.

Wydaje się, że jest pewne zamieszanie co do celu.

  • Jeden plik (to jest najważniejsza część)
  • Rozszerzenie albo .ps1albo.vbs
  • Zarówno POWERSHELLi VBScriptwewnątrz pojedynczego pliku
  • Premia:
    • Brak zapisu do pliku zewnętrznego
    • Przed każdą linią
    • Konieczność ucieczki ze znaków specjalnych w kodzie
    • Kodowanie całych sekcji skryptu (operacje obciążające procesor)

Oto przykład thoeretyczny:

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>

Coś takiego ->

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>

Odpowiedzi

2 vonPryz Aug 21 2020 at 13:44

Utwórz skrypt VBS w zwykły sposób. Zapisz w jakiejś lokalizacji, a następnie przekonwertuj go na Base64. Stosowane jest kodowanie bajtowe, dzięki czemu będzie działać również w przypadku plików binarnych i przezwycięża problemy z kodowaniem znaków. Tak jak tak

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

Następnie w skrypcie programu PowerShell uwzględnij zakodowaną wersję skryptu VBS. Przekonwertuj Base64 z powrotem na ciąg i zapisz go do pliku. Na koniec wywołaj, cscriptaby uruchomić .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 

Inną opcją jest osadzenie VBScript w łańcuchu tutaj, jak to,

# 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

Być może masz na myśli utworzenie .ps1pliku skryptu i uruchomienie go z vbscript?

Jeśli tak, oto przykład o nazwie Compress_Archive_by_Extension.vbs

Uwaga: Compress-Archive jest dostępna tylko z 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
'---------------------------------------------------------------------

Drugi przykład: Aby pobrać obraz z witryny: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
'----------------------------------------------------------------------------------------

EDYCJA: 21/08/2020 @ 20:45

Oto „pseudo-hybryda”, ponieważ wykorzystuje do wykonania plik tymczasowy: zainspirowany odpowiedzią @vonPryz.

Możesz go zapisać jako Test.ps1i wykonać z 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

Kolejny prosty przykład:

$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

Oto moja ostateczna odpowiedź, nie testowałem niczego bardzo skomplikowanego, więc nie jestem pewien, jak poradziłby sobie z takimi znakami jak znaki specjalne ...

#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

Możesz osadzić kod VB.NET w kodzie PowerShell za pomocą 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();

Nie do końca vbscript, ale jest dobrym rozwiązaniem.