Can this powershell command be condensed any further? I want to try to speed this up. (One liners sent from a batch script)

huangapple go评论63阅读模式
英文:

Can this powershell command be condensed any further? I want to try to speed this up. (One liners sent from a batch script)

问题

这是一个CMD脚本,我的主要问题是关于PS(但我肯定也会采纳CMD的建议,它确实有效)。它使用一个PowerShell命令作为一行命令来"提取"嵌入在其内的文件。(当您将文件拖放到它上面时,它会将它们嵌入到自身中,双击脚本以提取嵌入的文件)

提取命令速度较慢...我刚刚更新了它,之前它使用临时文件,但我能够通过在第一次使用相同正则表达式的情况下获取两个变量来获得我需要的两个变量,所以也许有一种方法可以在每次搜索整个文件时获取它们两个?

(已更新 2023年7月13日)
以下是完整的脚本BAG.cmd

@ECHO OFF & SET N=0
>nul 2>&1 REG ADD HKCU\Software\Classes\.Admin\shell\runas\command /f /ve /d "CMD /x /d /r SET \"f0=%%2\"& call \"%%2\" %%3"& SET _= %*
>nul 2>&1 FLTMC|| IF "%f0%" NEQ "%~f0" (CD.&>"%temp%\runas.Admin" & START "%~n0" /high "%temp%\runas.Admin" "%~f0" "%_:="%"%" & EXIT /b)
>nul 2>&1 REG DELETE HKCU\Software\Classes\.Admin\ /f
>nul 2>&1 DEL %temp%\runas.Admin /f
FOR /F "usebackq skip=2 tokens=3-4" %%i IN (`REG QUERY "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion" /v ProductName 2^nul`) DO SET "VER=%%i %%j"
IF NOT "%VER%"=="Windows 10" ECHO. & ECHO UNSUPPORTED SYSTEM DETECTED! & ECHO. & PAUSE & EXIT
IF [%1]==[] (CALL :EMPTYBAG) ELSE (ECHO FILLING BAG... & ECHO. & FOR %%i IN (%*) DO (CALL :FILLBAG %%i))
IF %N%==1 (ECHO. & PAUSE & EXIT) ELSE (EXIT)
:FILLBAG
IF EXIST %1\* ECHO [Folder - Ignored] - %~nx1 - Folders are not supported! & SET N=1 & EXIT /b
IF %~z1 LSS 1 ECHO [File   - Ignored] - %~nx1 - Empty files are not supported! & SET N=1 & EXIT /b
IF %~z1 GEQ 750000000 ECHO [File   - Ignored] - %~nx1 - Not Added! The file is too large! & SET N=1 & EXIT /b
SET /A Size=(%~z0 + %~z1) * (130 / 100)
IF %Size% GEQ 980000000 ECHO [File   - Ignored] - %~nx1 - Not Added! There is not enough room in the BAG! & SET N=1 & EXIT /b
ECHO.>>"%~f0" & POWERSHELL -nop -c "Add-Content '%~f0' ^"::%~nx1::^" -NoNewline; [Convert]::ToBase64String([IO.File]::ReadAllBytes(^"%~1^")) | Add-Content ^"%~f0^" -NoNewline; Add-Content '%~f0' ^"::%~nx1::^" -NoNewline" & DEL /F "%~1">nul
EXIT /b
:EMPTYBAG
IF %~z0 LSS 2205 ECHO The BAG is already empty, drag-and-drop something onto the BAG to put it inside. ;^) & ECHO. & PAUSE & EXIT /b
IF %~z0 GEQ 80000000 (ECHO EMPTYING BAG... ^(This may take a while^)) ELSE (ECHO EMPTYING BAG...)
POWERSHELL -nop -c "$file=Get-Content '%~f0'; $match=[regex]::Matches($file,'::([^:]+)::(.+?)::\1::') | Foreach-Object {$name=$_.Groups[1].Value; $fname=$name; while(Test-Path -Path \"%~dp0$fname\") { $n++; $fname=\"($n)$name\" }; $data=$_.Groups[2].Value; [IO.File]::WriteAllBytes(\"$fname\", [Convert]::FromBase64String($data))}; (Get-Content '%~f0' -TotalCount 22) | Set-Content '%~f0'">nul
EXIT /b

原始耗时较长的代码行是提取命令(已在上面更新):

POWERSHELL -nop -c $file=Get-Content '%~f0'; $name=[regex]::Match($file,'::([^:]+)::(.+?)::\1::').Groups[1].Value.Replace('::','').Trim(); $data=[regex]::Match($file,'::([^:]+)::(.+?)::\1::').Groups[2].Value.Replace('::','').Trim(); [IO.File]::WriteAllBytes(\"$name\", [Convert]::FromBase64String($data)); (get-content '%~f0' -totalcount 26) | set-content '%~dp0emptyBAG.cmd' >nul

嵌入/转换为B64的过程非常快,但提取/转换回原来的文件需要一段时间。

我已经测试过了1GB大小的文件,该脚本只需大约30秒来嵌入,但提取需要7-8分钟;P

也许一个良好的测试案例大小约为40MB,以查看延迟但不会太长。

我认为在提取命令期间我遍历了整个文件4次,1) 用Get-Content设置文件变量,2) 在变量中扫描第一个正则表达式以设置$name,3) 在变量中扫描第二个正则表达式以设置$data,以及4) 以流的方式输出名为$name的$data以创建最终文件。

我不知道如何使其更有效率 =/

**编辑 - 脚本现在正常工作(已在上面更新),

英文:

*script updated below to reflect suggested changes

This is a CMD script my main question is for PS(But would def take cmd advice too, it is working though). It's using a powershell command as a one liner to "extract" an embedded files from itself. (When you drag/drop files onto it, it will embed them into itself, double click the script to extract the embedded files)

The extract command is slow.. I just updated this and it used to have temp files but I was able to get both variables I needed by switching groups with the same regex once for the first variable and again for the second, so maybe there's a way to get them both without searching through the entire file for each one?

(UPDATED 7/13/23)
Here is the full script BAG.cmd

@ECHO OFF & SET N=0
>nul 2>&1 REG ADD HKCU\Software\Classes\.Admin\shell\runas\command /f /ve /d "CMD /x /d /r SET \"f0=%%2\"& call \"%%2\" %%3"& SET _= %*
>nul 2>&1 FLTMC|| IF "%f0%" NEQ "%~f0" (CD.>"%temp%\runas.Admin" & START "%~n0" /high "%temp%\runas.Admin" "%~f0" "%_:"=""%" & EXIT /b)
>nul 2>&1 REG DELETE HKCU\Software\Classes\.Admin\ /f
>nul 2>&1 DEL %temp%\runas.Admin /f
FOR /F "usebackq skip=2 tokens=3-4" %%i IN (`REG QUERY "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion" /v ProductName 2^>nul`) DO SET "VER=%%i %%j"
IF NOT "%VER%"=="Windows 10" ECHO. & ECHO UNSUPPORTED SYSTEM DETECTED! & ECHO. & PAUSE & EXIT
IF [%1]==[] (CALL :EMPTYBAG) ELSE (ECHO FILLING BAG... & ECHO. & FOR %%i IN (%*) DO (CALL :FILLBAG %%i))
IF %N%==1 (ECHO. & PAUSE & EXIT) ELSE (EXIT)
:FILLBAG
IF EXIST %1\* ECHO [Folder - Ignored] - %~nx1 - Folders are not supported! & SET N=1 & EXIT /b
IF %~z1 LSS 1 ECHO [File   - Ignored] - %~nx1 - Empty files are not supported! & SET N=1 & EXIT /b
IF %~z1 GEQ 750000000 ECHO [File   - Ignored] - %~nx1 - Not Added! The file is too large! & SET N=1 & EXIT /b
SET /A Size=(%~z0 + %~z1) * (130 / 100)
IF %Size% GEQ 980000000 ECHO [File   - Ignored] - %~nx1 - Not Added! There is not enough room in the BAG! & SET N=1 & EXIT /b
ECHO.>>"%~f0" & POWERSHELL -nop -c "Add-Content '%~f0' "^""::%~nx1::"^"" -NoNewline; [Convert]::ToBase64String([IO.File]::ReadAllBytes("^""%~1"^"")) | Add-Content "^""%~f0"^"" -NoNewline; Add-Content '%~f0' "^""::%~nx1::"^"" -NoNewline" & DEL /F "%~1">nul
EXIT /b
:EMPTYBAG
IF %~z0 LSS 2205 ECHO The BAG is already empty, drag-and-drop something onto the BAG to put it inside. ;^) & ECHO. & PAUSE & EXIT /b
IF %~z0 GEQ 80000000 (ECHO EMPTYING BAG... ^(This may take a while^)) ELSE (ECHO EMPTYING BAG...)
POWERSHELL -nop -c "$file=Get-Content '%~f0'; $match=[regex]::Matches($file,'::([^^:]+)::(.+?)::\1::') | Foreach-Object {$name=$_.Groups[1].Value; $fname=$name; while(Test-Path -Path "^""%~dp0$fname"^"") { $n++; $fname="^""($n)$name"^"" }; $data=$_.Groups[2].Value; [IO.File]::WriteAllBytes("^""$fname"^"", [Convert]::FromBase64String($data))}; (Get-Content '%~f0' -TotalCount 22) | Set-Content '%~f0'">nul
EXIT /b

The original line that took such a long time was the extraction command (updated above):

POWERSHELL -nop -c $file=Get-Content '%~f0'; $name=[regex]::Match^($file,'\:\:^([^^^\:]+^)\:\:^(.+?^)\:\:\1\:\:'^).Groups[1].Value.Replace^('::',''^).Trim^(^); $data=[regex]::Match^($file,'\:\:^([^^^\:]+^)\:\:^(.+?^)\:\:\1\:\:'^).Groups[2].Value.Replace^('::',''^).Trim^(^); [IO.File]::WriteAllBytes(\"$name\", [Convert]::FromBase64String($data)); ^(get-content '%~f0' -totalcount 26^) ^| set-content '%~dp0emptyBAG.cmd' >nul

Embedding/Converting to B64 happens very quick, but extracting/converting back to whatever it was takes a while.

Ive tested up to 1gb file with this script, only takes about 30 seconds to embed, but that takes 7-8min to extract ;P

Probably a good size for a test case would be ~40mb to see the delay but not have it be too long.

I think I am rolling through the entire file 4 times during the extraction command, 1) to set the file to a var with Get-Content, 2) to scan the var for the first regex to set $name, 3) to scan the var for the second regex to set $data, and 4) to stream out the $data named $name to create the final file..

I dont know how to make it more efficient =/

**EDIT - Script is working properly now(updated above), TY to all who helped.

答案1

得分: 1

以下是您给出的代码的翻译:

POWERSHELL -nop -c "$file=Get-Content ''%~f0'`; $match=[regex]::Matches($file,'::([^:]+)::(.+?)::\1::') | Foreach-Object {$name=$_.Groups[1].Value; $fname=$name; while(Test-Path -Path "`"%~dp0$fname`"") { $n++; $fname="($n)$name" }; $data=$_.Groups[2].Value; [IO.File]::WriteAllBytes("`"$fname`"", [Convert]::FromBase64String($data))}; (Get-Content ''%~f0'' -TotalCount 22) | Set-Content ''%~f0'`">>nul

希望对您有所帮助!

英文:

The original line I was working on was

POWERSHELL -nop -c $file=Get-Content '%~f0'; $name=[regex]::Match^($file,'\:\:^([^^^\:]+^)\:\:^(.+?^)\:\:\1\:\:'^).Groups[1].Value.Replace^('::',''^).Trim^(^); $data=[regex]::Match^($file,'\:\:^([^^^\:]+^)\:\:^(.+?^)\:\:\1\:\:'^).Groups[2].Value.Replace^('::',''^).Trim^(^); [IO.File]::WriteAllBytes(\"$name\", [Convert]::FromBase64String($data)); ^(get-content '%~f0' -totalcount 26^) ^| set-content '%~dp0emptyBAG.cmd' >nul

There were unnecessary escape chars because I could use """ in place of \" in the powershell commands to get the same effect and enclose the entire line in ". Doing this allowed me to remove the redundant ^ chars. Leaving me with this:

POWERSHELL -nop -c $file=Get-Content '%~f0'; $name=[regex]::Match($file,'\:\:([^^\:]+)\:\:(.+?)\:\:\1\:\:').Groups[1].Value.Replace^('::','').Trim(); $data=[regex]::Match($file,'\:\:([^^\:]+)\:\:(.+?)\:\:\1\:\:').Groups[2].Value.Replace('::','').Trim(); [IO.File]::WriteAllBytes("""$name""", [Convert]::FromBase64String($data)); (get-content '%~f0' -totalcount 26) | set-content '%~dp0emptyBAG.cmd'" >nul

With the line cleaned up and easier to work with the regex was changed from running twice to running once and being referenced by var:

POWERSHELL -nop -c $file=Get-Content '%~f0'; $match=[regex]::Match($file,'\:\:([^^\:]+)\:\:(.+?)\:\:\1\:\:'); $name = $match.Groups[1].Value.Replace^('::','').Trim(); $data = $match.Groups[2].Value.Replace('::','').Trim(); [IO.File]::WriteAllBytes("""$name""", [Convert]::FromBase64String($data)); (get-content '%~f0' -totalcount 26) | set-content '%~dp0emptyBAG.cmd'" >nul

But what would happen if the regex found multiple matches? At this point it would cycle through and stop after the first. So in order to use the same command but continue through all the matches the "Match" command was switched to "Matches" and a Foreach loop was used to cycle through each object, using $_ as the current object. (along with removing the trim statements due to better data input and changing the outfile name and totalcount # because the script is smaller/less lines now)

POWERSHELL -nop -c "$file=Get-Content '%~f0'; $match=[regex]::Matches($file,'\:\:([^^\:]+)\:\:(.+?)\:\:\1\:\:') | Foreach-Object {$name=$_.Groups[1].Value; $data=$_.Groups[2].Value; [IO.File]::WriteAllBytes("""$name""", [Convert]::FromBase64String($data))}; (Get-Content '%~f0' -TotalCount 17) | Set-Content '%~f0'">nul

EDIT:
I was able to remove more escape chars, I didnt need them between the : in the regex, but the regex did require one.

POWERSHELL -nop -c "$file=Get-Content '%~f0'; $match=[regex]::Matches($file,'::([^^:]+)::(.+?)::\1::') | Foreach-Object {$name=$_.Groups[1].Value; $fname=$name; while(Test-Path -Path """%~dp0$fname""") { $n++; $fname="""($n)$name""" }; $data=$_.Groups[2].Value; [IO.File]::WriteAllBytes("""$fname""", [Convert]::FromBase64String($data))}; (Get-Content '%~f0' -TotalCount 22) | Set-Content '%~f0'">nul

Thank you all for your help!

huangapple
  • 本文由 发表于 2023年7月11日 06:02:08
  • 转载请务必保留本文链接:https://go.coder-hub.com/76657604.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定