programing

윈도우즈 배치 파일 내에서 PowerShell 스크립트를 실행하는 방법

kingscode 2023. 8. 29. 21:27
반응형

윈도우즈 배치 파일 내에서 PowerShell 스크립트를 실행하는 방법

Windows 배치 스크립트와 동일한 파일에 PowerShell 스크립트가 내장되어 있는 방법은 무엇입니까?

저는 다른 시나리오에서도 이러한 일이 가능하다는 것을 알고 있습니다.

  • 다을사용여배포스에치함트 SQL을 내장sqlcmd goto와 했습니다.
  • 첫를 들어 *nix 환경트의첫립줄있에스가는이경실다, 를들설니합명같이다과예와 같이 됩니다.#!/usr/local/bin/python.

이를 수행할 방법이 없을 수도 있습니다. 이 경우 시작 스크립트와 별도의 PowerShell 스크립트를 호출해야 합니다.

제가 생각한 한 가지 가능한 해결책은 PowerShell 스크립트를 에코아웃한 다음 실행하는 것입니다.이렇게 하지 않는 좋은 이유는 이러한 시도를 하는 이유의 일부는 를 들어 이스케이프 문자의 고통 없이 PowerShell 환경의 이점을 사용하기 위한 것이기 때문입니다.

저는 몇 가지 특이한 제약이 있어서 우아한 해결책을 찾고 싶습니다.저는 이 질문이 다양한 사람들의 반응을 미끼로 하는 것일 수도 있다고 생각합니다: "대신 이 다른 문제를 해결하려고 노력하는 것이 어때요?"이것들이 저의 제약이라고만 하면 됩니다, 죄송합니다.

아이디어 있어요?이를 달성할 수 있는 현명한 의견과 탈출 캐릭터의 적절한 조합이 있습니까?

이를 달성하는 방법에 대한 몇 가지 생각:

  • 럿캐.^Visual Basic ▁an▁in▁like▁의 밑줄과 같은 연속이 있습니다. Visual Basic의 밑줄과 같은 것입니다.
  • ▁an.&을 구분할 때 합니다.echo Hello & echo World으로 두 개의 에 발생합니다.
  • %0은 현재 실행 중인 스크립트를 제공합니다.

그래서 (제가 그것을 작동시킬 수 있다면) 다음과 같은 것이 좋을 것입니다.

# & call powershell -psconsolefile %0
# & goto :EOF
/* From here on in we're running nice juicy powershell code */
Write-Output "Hello World"

제외하고는...

  • 효과가 없어요...왜냐면
  • 가 PowerShell의 PowerShell의 경우에는 다음과 같습니다.Windows PowerShell console file "insideout.bat" extension is not psc1. Windows PowerShell console file extension must be psc1.
  • 이 . CMD에 가 있기는 말입니다. 하지만 우연히 발견되기는 합니다.'#', it is not recognized as an internal or external command, operable program or batch file.

이 제품은 PowerShell에 올바른 라인만 전달합니다.

dosps2.cmd:

@findstr/v "^@f.*&" "%~f0"|powershell -&goto:eof
Write-Output "Hello World" 
Write-Output "Hello some@com & again" 

정규식은 다음으로 시작하는 행을 제외합니다.@f 그고포함서해리▁an서▁including를 포함합니다.&다른 모든 것을 PowerShell에 전달합니다.

C:\tmp>dosps2
Hello World
Hello some@com & again

때때로 "다중 언어 스크립트"라고 불리는 것을 찾는 것처럼 들립니다.CMD -> PowerShell의 경우

@@:: This prolog allows a PowerShell script to be embedded in a .CMD file.
@@:: Any non-PowerShell content must be preceeded by "@@"
@@setlocal
@@set POWERSHELL_BAT_ARGS=%*
@@if defined POWERSHELL_BAT_ARGS set POWERSHELL_BAT_ARGS=%POWERSHELL_BAT_ARGS:"=\"%
@@PowerShell -Command Invoke-Expression $('$args=@(^&{$args} %POWERSHELL_BAT_ARGS%);'+[String]::Join([char]10,$((Get-Content '%~f0') -notmatch '^^@@'))) & goto :EOF

인용된 인수를 지원할 필요가 없는 경우에는 한 줄로 묶을 수도 있습니다.

@PowerShell -Command Invoke-Expression $('$args=@(^&{$args} %*);'+[String]::Join([char]10,(Get-Content '%~f0') -notmatch '^^@PowerShell.*EOF$')) & goto :EOF

http://blogs.msdn.com/jaybaz_ms/archive/2007/04/26/powershell-polyglot.aspx 에서 가져온 것입니다.이것이 PowerShell v1입니다. v2에서는 더 간단할 수 있지만, 저는 찾아보지 않았습니다.

여기에서는 이 주제에 대해 논의했습니다.주요 목표는 느린 I/O 작업을 줄이고 중복 출력 없이 스크립트를 실행하기 위해 임시 파일을 사용하지 않는 것이었습니다.

그리고 저에 따르면 최고의 해결책은 다음과 같습니다.

<# :
@echo off
setlocal
set "POWERSHELL_BAT_ARGS=%*"
if defined POWERSHELL_BAT_ARGS set "POWERSHELL_BAT_ARGS=%POWERSHELL_BAT_ARGS:"=\"%"
endlocal & powershell -NoLogo -NoProfile -Command "$input | &{ [ScriptBlock]::Create( ( Get-Content \"%~f0\" ) -join [char]10 ).Invoke( @( &{ $args } %POWERSHELL_BAT_ARGS% ) ) }"
goto :EOF
#>

param(
    [string]$str
);

$VAR = "Hello, world!";

function F1() {
    $str;
    $script:VAR;
}

F1;

훨씬 더 나은 방법(여기 참조):

<# : batch portion (begins PowerShell multi-line comment block)


@echo off & setlocal
set "POWERSHELL_BAT_ARGS=%*"

echo ---- FROM BATCH
powershell -noprofile -NoLogo "iex (${%~f0} | out-string)"
exit /b %errorlevel%

: end batch / begin PowerShell chimera #>

$VAR = "---- FROM POWERSHELL";
$VAR;
$POWERSHELL_BAT_ARGS=$env:POWERSHELL_BAT_ARGS
$POWERSHELL_BAT_ARGS

POWERSHELL_BAT_ARGS명령행 인수는 배치 부분에서 변수로 처음 설정됩니다.

우선 . - 이 줄은 "" "" " " " " " " 입니다.<# : 석될것다니입처다것처럼 구문 됩니다.:<#리디렉션이 다른 명령보다 우선 순위가 높기 때문입니다.

하만시작대로 :배치 파일은 레이블로 사용됩니다. 즉, 실행되지 않습니다.여전히 PowerShell에 대한 설명은 유효합니다.

이 제대로 할 수 것만 남았습니다.%~f0이 경로는 cmd.exe에 의해 실행되는 스크립트의 전체 경로입니다.

처음에 PowerShell에서 한 가지 오류가 발생하는 것을 개의치 않는다면 이 방법이 효과적인 것 같습니다.

dosps.cmd:

@powershell -<%~f0&goto:eof
Write-Output "Hello World" 
Write-Output "Hello World again" 

저는 장 프랑수아 라부아르의 솔루션을 매우 좋아하는데, 특히 그가 Arguments를 처리하고 powershell-script에 과감하게 전달했기 때문입니다(+1 추가).

하지만 한 가지 결점이 있습니다.제가 언급할 명성이 없기 때문에, 저는 새로운 해결책으로 수정 사항을 게시합니다.

된 경우 .$파일 내용을 로드하기 전에 평가할 문자입니다.가장 간단한 해결책은 큰따옴표를 바꾸는 것입니다.

PowerShell -c ^"Invoke-Expression ('^& {' + [io.file]::ReadAllText('%~f0') + '} %ARGS%')"

개인적으로, 저는 오히려 사용하는 것을 선호합니다.get-content-raw나에게 있어 이것은 더 강력한 것입니다.

PowerShell -c ^"Invoke-Expression ('^& {' + (get-content -raw '%~f0') + '} %ARGS%')"

하지만 그건 물론 제 개인적인 생각입니다.ReadAllText works아주 완벽하게

완전성을 위해 수정된 스크립트:

<# :# PowerShell comment protecting the Batch section
@echo off
:# Disabling argument expansion avoids issues with ! in arguments.
setlocal EnableExtensions DisableDelayedExpansion

:# Prepare the batch arguments, so that PowerShell parses them correctly
set ARGS=%*
if defined ARGS set ARGS=%ARGS:"=\"%
if defined ARGS set ARGS=%ARGS:'=''%

:# The ^ before the first " ensures that the Batch parser does not enter quoted mode
:# there, but that it enters and exits quoted mode for every subsequent pair of ".
:# This in turn protects the possible special chars & | < > within quoted arguments.
:# Then the \ before each pair of " ensures that PowerShell's C command line parser 
:# considers these pairs as part of the first and only argument following -c.
:# Cherry on the cake, it's possible to pass a " to PS by entering two "" in the bat args.
echo In Batch
PowerShell -c ^"Invoke-Expression ('^& {' + (get-content -raw '%~f0') + '} %ARGS%')"
echo Back in Batch. PowerShell exit code = %ERRORLEVEL%
exit /b

###############################################################################
End of the PS comment around the Batch section; Begin the PowerShell section #>

echo "In PowerShell"
$Args | % { "PowerShell Args[{0}] = '$_'" -f $i++ }
exit 0

또한 내장된 PowerShell 및/또는 VBScript/JScript 코드를 지원하는 이 "polyglot" 래퍼 스크립트를 고려해 보십시오. 이 스크립트는 2013년에 작성자 자신인 flabdablet가 게시한 독창적인 원본에서 수정되었지만 링크 전용 답변이었기 때문에 2015년에 삭제되었습니다.

Kyle의 훌륭한 답변을 개선하는 솔루션:

파일 배파을만예다니듭일치)을합니다.sample.cmd)는과 같은내용을 있습니다

<# ::
@echo off & setlocal
copy /y "%~f0" "%TEMP%\%~n0.ps1" >NUL && powershell -NoProfile -ExecutionPolicy Bypass -File "%TEMP%\%~n0.ps1" %*
set ec=%ERRORLEVEL% & del "%TEMP%\%~n0.ps1"
exit /b %ec%
#>

# Paste arbitrary PowerShell code here.
# In this example, all arguments are echoed.
'Args:'
$Args | % { 'arg #{0}: [{1}]' -f ++$i, $_ }

참고:

  • 될 때 로 사용되는 파일입니다.*.ps1은 파이생정경에 됩니다.%TEMP%폴더; 그렇게 함으로써 단순히 사용함으로써 (합리적으로) 강력하게 인수를 전달하는 것을 크게 단순화합니다.%*
  • 위의 명령은 Windows PowerShell을 호출합니다.크로스 플랫폼 PowerShell(Core) v7+ 에디션을 호출하려면 다음과 같이 대체합니다.powershell와 함께pwsh위의 암호로

기술 설명:

  • <# ::블록의 이지만 PowerShell의 경우는 다음과 같습니다.cmd.exe무시, npocmaka의 대답에서 차용한 기술입니다.

  • 시어음작으로 @ PowerShell에서는되지만 PowerShell에서는 됩니다.cmd.exe 회전이 @- 접미사가 없는 행은 다음으로 끝납니다.exit /b거기서 종료하는, 바로 파종료을하고일배치서거기▁which▁right,고▁the하.cmd.exe 는 나머지 파일을 무시하므로 배치되지 않은 파일 코드, 즉 PowerShell 코드를 자유롭게 포함할 수 있습니다.

  • #>코드를 합니다. line은 PowerShell 주석 블록을 포함합니다.

  • 파일이기 형식의 PowerShell 파일입니다.findstr코드를 은 파일 확장자가 입니다. 그러나 PowerShell은 파일 이름 확장자가 있는 스크립트만 실행하기 때문입니다..ps1배치 파일의 (임의의) 사본을 작성해야 합니다.%TEMP%\%~n0.ps1을 임시복생다에 만듭니다.%TEMP%배치 파일에 대해 이름이 지정된 폴더(%~n0), 확장자가 경우), 이 파일은 삭제됩니다..ps1작업이 완료되면 임시 파일이 자동으로 제거됩니다.

  • 다음의 3개의 개별 라인에 주의하십시오.cmd.exe명령문은 PowerShell 명령의 종료 코드를 전달하는 데 필요합니다.
    (사用)setlocal enabledelayedexpansion가정적으로 그것을 한 줄로 하는 것을 허용하지만, 그것은 원치 않는 해석을 초래할 수 있습니다.!chars. 인수에서.)


인수 전달의 견고성을 입증하기 위해:

가 의위코다음같과이저로 저장되었다고 합니다.sample.cmd호출:

sample.cmd "val. w/ spaces & special chars. (\|<>'), on %OS%" 666 "Lisa \"Left Eye\" Lopez"

다음과 같은 결과를 제공합니다.

Args:
arg #1: [val. w/ spaces & special chars. (\|<>'), on Windows_NT]
arg #2: [666]
arg #3: [Lisa "Left Eye" Lopez]

포함된 문자가 로 전달된 방식을 기록합니다.
그러나 포함된 문자와 관련에지 사례있습니다.:

:: # BREAKS, due to the `&` inside \"...\"
sample.cmd "A \"rock & roll\" life style"

:: # Doesn't break, but DOESN'T PRESERVE ARGUMENT BOUNDARIES.
sample.cmd "A \""rock & roll\"" life style"

이러한 어려움은 의 결함 있는 주장 구문 분석에 기인하며, 궁극적으로 flabdablet가 그의 훌륭한 답변에서 지적한 것처럼 이러한 결함시도하는 것은 무의미합니다.

그가 설명했듯이, 시퀀스 안에 (sic)이 있는 다음 메타 문자를 이스케이프하면 문제가 해결됩니다.

& | < >

위의 예를 사용합니다.

:: # OK: cmd.exe metachars. inside \"...\" are ^^^-escaped.
sample.cmd "A \"rock ^^^& roll\" life style"

한 솔루션과 를 지원하며 줄 param제이가 올린 해결책처럼.유일한 단점은 이 솔루션이 임시 파일을 만든다는 것입니다.저의 사용 사례에 적합합니다.

@@echo off
@@findstr/v "^@@.*" "%~f0" > "%~f0.ps1" & powershell -ExecutionPolicy ByPass "%~f0.ps1" %* & del "%~f0.ps1" & goto:eof

이 작업에 대한 현재 선호도는 mklement0의 첫 번째 솔루션과 거의 동일한 방식으로 작동하는 폴리글롯 헤더입니다.

<#  :cmd header for PowerShell script
@   set dir=%~dp0
@   set ps1="%TMP%\%~n0-%RANDOM%-%RANDOM%-%RANDOM%-%RANDOM%.ps1"
@   copy /b /y "%~f0" %ps1% >nul
@   powershell -NoProfile -ExecutionPolicy Bypass -File %ps1% %*
@   del /f %ps1%
@   goto :eof
#>

# Paste arbitrary PowerShell code here.
# In this example, all arguments are echoed.
$Args | % { 'arg #{0}: [{1}]' -f ++$i, $_ }

여러 가지 이유로 cmd 헤더를 각각 하나의 명령어로 여러 줄로 배치하는 것을 선호합니다.먼저, 무슨 일이 일어나고 있는지를 보는 것이 더 쉽다고 생각합니다. 명령줄은 편집 창 오른쪽에서 떨어지지 않을 정도로 짧습니다. 왼쪽에 있는 구두점 열은 첫 번째 줄에 있는 끔찍하게 남용된 레이블이 말하는 머리글 블록으로 시각적으로 표시합니다. 째둘,,del그리고.goto명령은 고유한 줄에 있으므로, 정말 펑키한 것이 스크립트 인수로 전달되더라도 계속 실행됩니다.

임시적인 솔루션을 선호하게 되었습니다..ps1존하는사람대다줄을게에에 을 제출합니다.Invoke-ExpressionPowerShell의 이해할 수 없는 오류 메시지에는 최소한 의미 있는 라인 번호가 포함되기 때문입니다.

데 까지 완전히 의 PowerShell은 PowerShell과 128비트를 사용합니다.%RANDOM%임시 파일 이름에 포함되어 있기 때문에 여러 개의 동시 스크립트가 서로의 임시 파일을 중지하지 않습니다.은 원래 스크립트가가 손실될 수 입니다. 는 cmd 파일 접근 방식에 입니다. 이는 다음과 같은 이유입니다.dir환경 변수가 두 번째 줄에 생성되었습니다.

PowerShell이 스크립트 파일에서 허용할 파일 이름 확장자에 대해 그렇게 신경 쓰지 않는 것은 훨씬 덜 성가실 것입니다. 하지만 당신은 당신이 갖고 싶은 셸이 아니라 당신이 가지고 있는 셸과 전쟁을 합니다.

말이 나온 김에: mklement0이 관찰하듯이,

# BREAKS, due to the `&` inside \"...\"
sample.cmd "A \"rock & roll\" life style"

은 실은제깨다집면니, 왜하냐로 .cmd.exe전혀 가치 없는 인수 구문 분석입니다.저는 일반적으로 cmd의 많은 제한 사항을 숨기려고 노력하는 작업이 적을수록 예상치 못한 버그가 줄어든다는 것을 발견했습니다(예를 들어, mkemle0의 흠잡을 데 없는 앰퍼와 논리 탈출을 깨는 괄호가 포함된 인수를 생각해 낼 수 있을 것이라고 확신합니다).제가 보기에, 총알을 깨물고 비슷한 것을 사용하는 것이 덜 고통스럽습니다.

sample.cmd "A \"rock ^^^& roll\" life style"

와 세 번째 첫와째세째번번.^ 분석될 때, 두은 이스케이프를 합니다.&으로 powershell.exe 못생겼어요.", 이못겼요어생네거요▁yes어못겼생.네, 그렇게 행동하는 것이 더 어렵습니다.cmd.exe대본에서 가장 먼저 시작하는 것이 아닙니다.걱정 말아요.중요한 경우 문서화합니다.

의 실제 대분의실이션서에케애는&어쨌든 이슈는 미결입니다.이와 같은 스크립트에 대한 인수로 전달될 대부분은 드래그 앤 드롭을 통해 도착하는 경로 이름이 될 것입니다.Windows에서는 공간과 앰퍼샌드를 보호하기에 충분한 따옴표를 따옴표 이외의 값을 따옴표로 묶습니다. 이 값은 Windows 경로 이름에서는 사용할 수 없습니다.

도 못 Vinyl LP's, 12"CSV 파일에 표시됩니다.

다른 샘플 배치+검정력셸 스크립트...이 솔루션은 다른 제안 솔루션보다 간단하며 다음과 같은 특징을 가지고 있습니다.

  • 임시 파일 생성 없음 => 성능이 향상되고 덮어쓸 위험이 없습니다.
  • 배치 코드의 특별한 접두사는 없습니다.이것은 그냥 일반적인 배치입니다.PowerShell 코드도 마찬가지입니다.
  • ! % < > ' $와 같은 까다로운 문자가 있는 따옴표로 묶인 문자열을 포함하여 모든 배치 인수를 PowerShell에 올바르게 전달합니다.
  • 큰따옴표는 큰따옴표를 두 배로 하여 전달할 수 있습니다.
  • 표준 입력은 PowerShell에서 사용할 수 있습니다. (Batch 자체를 PowerShell로 파이프로 연결하는 모든 버전과는 대조적입니다.)

이 샘플은 언어 전환을 표시하고 PowerShell 측은 배치 측에서 받은 인수 목록을 표시합니다.

<# :# PowerShell comment protecting the Batch section
@echo off
:# Disabling argument expansion avoids issues with ! in arguments.
setlocal EnableExtensions DisableDelayedExpansion

:# Prepare the batch arguments, so that PowerShell parses them correctly
set ARGS=%*
if defined ARGS set ARGS=%ARGS:"=\"%
if defined ARGS set ARGS=%ARGS:'=''%

:# The ^ before the first " ensures that the Batch parser does not enter quoted mode
:# there, but that it enters and exits quoted mode for every subsequent pair of ".
:# This in turn protects the possible special chars & | < > within quoted arguments.
:# Then the \ before each pair of " ensures that PowerShell's C command line parser 
:# considers these pairs as part of the first and only argument following -c.
:# Cherry on the cake, it's possible to pass a " to PS by entering two "" in the bat args.
echo In Batch
PowerShell -c ^"Invoke-Expression ('^& {' + [io.file]::ReadAllText(\"%~f0\") + '} %ARGS%')"
echo Back in Batch. PowerShell exit code = %ERRORLEVEL%
exit /b

###############################################################################
End of the PS comment around the Batch section; Begin the PowerShell section #>

echo "In PowerShell"
$Args | % { "PowerShell Args[{0}] = '$_'" -f $i++ }
exit 0

다른 대부분의 사람들이 실제로 PowerShell 주석처럼 보이듯이 배치 주석에는 :: 대신 :#을 사용합니다. (또는 다른 대부분의 스크립트 언어 주석처럼 보입니다.)

사용하다Invoke-Command(icm한 cmd로 만들 수 있습니다: " 여서줄", "다음 4헤를 ps1파일여유한효하배추다가치있니로습만들수줄에더1▁for줄),▁we다▁cmd▁4▁prep▁can니다▁line▁to▁batch배있한음습),줄:▁header▁short,치▁make▁it수.

<# : batch portion
@powershell -noprofile "& {icm -ScriptBlock ([Scriptblock]::Create((cat -Raw '%~f0'))) -NoNewScope -ArgumentList $args}" %*
@exit /b %errorlevel%
: end batch / begin powershell #>

"Result:"
$args | %{ "`$args[{0}]: $_" -f $i++ }

가 스크립트 [0]를 변경합니다.%*"'%~f0'" %*

당신의 질문을 완전히 이해하지 못한다면, 제 제안은 다음과 같습니다.

@echo off
set MYSCRIPT="some cool powershell code"
powershell -c %MYSCRIPT%

아니면 더 나은.

@echo off
set MYSCRIPTPATH=c:\work\bin\powershellscript.ps1
powershell %MYSCRIPTPATH%

Powershell 스크립트 앞에 세 줄을 추가하고, 블록 주석만 사용한 다음 배치 파일로 저장할 수 있습니다.그런 다음 Powershell 스크립트를 실행할 배치 파일을 가질 수 있습니다.예:

psscript.bat

@echo off
@powershell -command "(Get-Content -Encoding UTF8 '%0' | select-string -pattern '^[^@]')" | @powershell -NoProfile -ExecutionPolicy ByPass
@goto:eof
<# Must use block comment; Powershell script starts below #>
while($True) {
    Write-Host "wait for 3s"
    Start-Sleep -Seconds 3
}

몇 가지 아이디어를 모으기

<# :
@powershell -<%~f0&goto:eof
#>

Write-Output "Hello World" 
Write-Output "Hello World again" 
@powershell -noninteractive "& ([Scriptblock]::Create((gc '%~df0' | select -Skip 1 | Out-String))) %*" & goto :eof

제가 드리는 제물은 다음과 같습니다.

  • 확실한 것을 사용합니다.exe 입력 파일을 해시하여 임시로 파일 이름을 형성합니다.
  • more.exe를 사용하여 헤더를 제거하고 임시로 파일을 만듭니다.
  • all -arg xxx 스타일 인수를 powershell에 전달합니다.
  • 에서는 ps1 파일을 볼 수 있도록 임시로 보관합니다.

용법의 예


.\runner.bat
.\runner.bat -scope town
.\runner.bat -scope "whole universe"

주자.배트

@echo off & (For /F Delims^= %%a In ('CertUtil -HashFile %0 SHA1^|FindStr /VRC:"[^a-f 0-9]"') Do Set "PS1=%TEMP%\%%a.ps1" )
(if not exist %PS1% more +3 %0 > %PS1%) & (PowerShell.exe -ExecutionPolicy bypass -file %PS1% %* & goto :EOF)
@@@@@@[ PowerShell Starts Here ]@@@@@@

Param(
    [String]$scope = "world"
)
write-host "hello $scope"


레드 라이딩 후드의 답변을 수정스타일입니다.[ 스택 오버플로 편집 대기열이 가득 찼기 때문에 :D ]

완성도를 위해:

  • 누락된 유용한 PowerShell 인수가 추가되었습니다.
  • 오류 처리를 개선하기 위해 오류 수준을 반환합니다.
  • 단일 및 이중 따옴표로 묶인 인수 지원
  • 불쾌한 버그를 제거합니다.
  • Batch 및 PowerShell 구문 강조 표시와의 호환성을 보장하여 구문 오류를 방지합니다.

코드 조각:

<# :
@PowerShell -ExecutionPolicy Bypass -NoLogo -NoProfile "& ([Scriptblock]::Create((Get-Content '%~df0' | Select-Object -Skip 4 | Out-String))) %*" & goto :eof
exit /b %errorlevel%
#>

전체 데모 스크립트:

<# :
@PowerShell -ExecutionPolicy Bypass -NoLogo -NoProfile "& ([Scriptblock]::Create((Get-Content '%~df0' | Select-Object -Skip 4 | Out-String))) %*" & goto :eof
exit /b %errorlevel%
#>
Write-Output $PSVersionTable
Write-Output $args
$host.ui.RawUI.WindowTitle = "PowerShell Chimera"
[System.Security.Principal.WindowsIdentity]::GetCurrent().Name
Start-Sleep -Seconds 5
Exit 0x539

사용 예:

# demo.bat var1="1" var2='2' & echo %errorlevel%
# demo.bat "var1=1" 'var2=2' & echo %errorlevel%

출력:

Name                           Value
----                           -----
PSVersion                      5.1.19041.868
PSEdition                      Desktop
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0...}
BuildVersion                   10.0.19041.868
CLRVersion                     4.0.30319.42000
WSManStackVersion              3.0
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1
var1=1
var2=2
WINDOWS-PC\User
1337

참고 사항:만약 당신이 그것에 대해 확신하지 못한다면.-NonInteractive레드 라이딩 후드의 원래 답변에 사용된 주장, 아래 설명을 인용했습니다.

당신이 사용하는 것을 오해하고 있는 것 같습니다.-NonInteractive스위치; 당신은 여전히 powershell-non-interactive를 실행할 수 있고 대화형 프롬프트를 받을 수 있습니다.NonInteractive스위치는 자동화된 스크립팅 시나리오를 위해 고안되었으며, 파워셸이 사용자에게 프롬프트를 보내고 응답을 기다리지 않도록 할 수 있습니다.들어 이 아닌 에서 PowerShell을 하는 Get-Credential매개 변수가 없으면 사용자 이름과 암호를 묻는 대신 즉시 실패합니다.비대화형은 보안 메커니즘으로 작동하지 않습니다.더 나은 방법은 사용자가 액세스하는 데 사용할 수 있는 도구가 아니라 보호하려는 내용을 보호하는 것입니다.

출처: "PowerShell이 대화형 명령 창을 허용하지 않도록 강제하는 방법" 질문에 대한 jbsmith의 답변


PS: PowerShell 해킹대회로 변신한 주제 :D

1위를 차지하는 답변에는 당사 솔루션의 다음과 같은 기능이 없습니다.

  • Read-Host/pause이제 작동합니다.
  • 인수 패스스루(따옴표로 묶은 인수 포함)
  • 반환 코드 패스스루
<# :
  @set batch_args=%*
  @powershell "iex ('$args = @(iex \"^& {`$args} $env:batch_args\");' + (cat -Raw '%~f0'))"
  @exit /b %ERRORLEVEL%
#>

Write-Host Your PowerShell code goes here. First arg: $args[0] -fore Green
Read-Host Waiting for user to press Enter
Exit 42

인수가 필요하지 않으면 다음과 같이 하면 됩니다.@powershell "iex (cat -Raw '%~f0')"


설명:

  • <# ... #>. PowerShell은 다음과 같습니다.<# :배치에서 null 리디렉션으로 해석되므로 주석은 배치 코드로만 실행됩니다.
    • exit /b %ERRORLEVEL% 종료 코드 PowerShell 종료반록도합니다하를 .
  • 은 PowerShell 가 있는 만 실행합니다..ps1을 직접 실행하는 "PowerShell", "PowerShell"이라고 .Invoke-Expression/iex대신 그것을 실행하기 위해.
    • %~f0는 Batch의 코드 입니다.
    • $args인수를 이지만, 하여 수동으로합니다. PowerShell은 이 변수를 PowerShell과 동일하게 설정합니다.
  • @.
  • %*일괄 처리에서 스크립트에 전달된 모든 인수를 참조합니다.
    • & {$args} %*일부 볼 수 인수를 합니다.
    • PowerShell이 의심되므로 인용된 인수가 해당 스니펫에서 작동하지 않습니다.
    • 내가 하면,$args = (echo %*) echo는 가 전달되지 경우 의 상호 작용을 echo echo 는수달인전을않지되우다경상기 :/
    • 따라서 env 변수를 설정하고 Invoke-Expression으로 두 번 구문 분석하는 나의 초특급 해결 방법은 잘못된 인용 문제를 해결하고 인수 없음 문제를 방지합니다!
    • @ 0개의 인수가 있더라도 항상 배열을 얻을있습니다.

ScriptBlock기반 답변은 다음과 같은 문제를 가지고 있습니다.Write-Host은 "문"과 함께 됩니다.Write-Output/echo- 기반 답변은 블록이 완료될 때만 표시되며, 이는 내 사용 사례에 치명적입니다(사용).Read-Host할 경우 둡니다).

언급URL : https://stackoverflow.com/questions/2609985/how-to-run-a-powershell-script-within-a-windows-batch-file

반응형