[英]Pass argument from Powershell to Batch script
我需要將帶有特殊字符的密碼從 powershell 腳本automation.ps1
傳遞給批處理腳本batch_script.bat
,然后將其通過管道傳輸到main.py
。 從batch_script.bat
到main.py
的管道工作正常,即身份驗證成功。 但是,當我運行上述整個過程時,身份驗證失敗,但echo
密碼顯示正確的密碼字符串。
我的猜測是特殊字符存在問題。 傳遞這些字符串的安全方法是什么?
背景
我想通過 Python 腳本main.py
自動從某些外部源進行每日下載。 此過程需要密碼。 因此,我編寫了一個batch_script.bat
,它在提示輸入密碼時將密碼傳送到 Python 腳本。 但是,我不想將密碼作為純文本存儲在批處理腳本中,因此我對密碼進行了加密並編寫了另一層automation.ps1
來解密密碼並將其作為純文本傳遞給batch_script.bat
。
自動化.ps1
# get password
$securePassword = Get-Content "<file_path>" | ConvertTo-SecureString
$credentials = New-Object System.Management.Automation.PsCredential("<user_name>",$securePassword)
$unsecurePassword = ($credentials).GetNetworkCredential().Password
# call script
$script = "<path>\batch_script.bat"
$args = @("`"<user name>`"","`"$unsecurePassword`"")
start-process $script $args
批處理腳本.bat
(我知道在這個例子中我丟棄了傳遞的用戶名,只是想保留我傳遞多個 arguments 的事實,以防與它有任何相關性)
@echo off
SET username=%~1
SET password=%~2
echo(%password%|python main.py
有了以下,所有特殊字符都應該得到很好的處理。 如果需要轉義任何字符,請檢查此
$pass 可以是任何字符串,但檢查powershell的特殊字符
$pass="%^&<>|'\``,;=(\)![]\/";
# Wait till it ends with -Wait when using -NoNewWindow.
# It may be comprehensible to use `" instead of "" to denote we are enclosing string in quotes.
(thanks @mklement0 for elaboration).
start-process -Wait -NoNewWindow .\script.cmd "`"$pass`""
script.cmd
setlocal
rem Remove Double quotes
set "arg=%~1"
rem Test result with base64 encoding
echo|set/p="%arg%"|openssl base64
rem echo is used with set/p to prevent trailing new line.
echo|set/p="%arg%"|python main.py
rem Test with following, argument is in double quotes
rem script "%^&<>|'\`,;=(\)![]\/"
rem Expected result
rem %^&<>|'\`,;=(\)![]\/
tl;博士:
除非您特別需要批處理文件在新的 window中運行,否則請避免Start-Process
(其內置別名是start
和saps
),並直接調用批處理文件。
為避免$unsecurePassword
中的特殊字符出現問題,請勿將其作為參數傳遞,而是通過標准輸入(標准輸入流)傳遞,您的批處理文件將通過該標准輸入到您的python
腳本:
automation.ps1
:
# ...
$script = "<path>\batch_script.bat"
# Pass the password via *stdin*
$unsecurePassword | & $script 'userName'
注意: $OutputEncoding
首選項變量控制 PowerShell 用於將文本發送到外部程序的標准輸入的字符編碼。 在Windows PowerShell中,該變量默認為 ASCII(,) 編碼,這意味着在 Unicode 字符的 7 位 ASCII 范圍之外的任何字符,如轉重音?
幸運的是,PowerShell [Core] v6+ 現在默認為 UTF-8。 根據需要將所需的編碼分配給$OutputEncoding
。
batch_script.bat
:
@echo off
SET username=%~1
REM The batch file will pass its own stdin input through to Python.
python main.py
繼續閱讀背景信息。
除非您確實需要在新的 window中啟動批處理文件,否則最好的方法是直接從 PowerShell 調用它; 這樣,它運行:
在同一個控制台 window 中,同步。
其 output 流連接到 PowerShell(允許您捕獲或重定向輸出)。
因為您的批處理文件路徑存儲在變量中,所以直接調用需要使用&
,即調用運算符:
# Note: The " chars. around $unsecurePassword are only needed if the variable
# value contains cmd.exe metacharacters - see next section.
& $script 'userA' `"$unsecurePassword`"
Start-Process
通常是調用控制台應用程序、批處理文件和其他基於控制台的腳本的錯誤工具; 有關更多信息,請參閱此答案。
如果您確實需要在新的 window 中運行批處理文件(這只是Windows上的一個選項),請按如下方式使用Start-Process
(該命令將異步執行,除非您還通過-Wait
):
# The string with the arguments to pass is implicitly bound
# to the -ArgumentList parameter. Use only " for embedded quoting.
Start-Process $script "userA `"$unsecurePassword`""
注意:雖然(隱含的) -ArgumentList
( -Args
) 參數是數組值 ( [string[]]
) 並且單獨傳遞 arguments 可以說是更清潔的方法,但這通常不能正常工作,因為可能存在一個長期存在的錯誤不會得到修復; 例如,
Start-Process foo.exe -Args 'one', 'two (2)'
通過3 arguments 而不是 2; 也就是說,它將單個字符串'two (2)'
作為兩個arguments 傳遞 - 請參閱此 GitHub 問題。
因此,將帶有嵌入式引用的單個參數傳遞給-ArgumentList
最終更簡單且更可預測,但請確保僅使用"
(而不是'
)進行嵌入式引用:
Start-Process foo.exe -Args "one `"two (2)`""
cmd.exe
/ 批處理文件:筆記:
cmd.exe
(解釋批處理文件的傳統命令處理器)的局限性阻礙了完全穩健的解決方案; 值得注意的是,當您從 PowerShell 調用批處理文件時,您不能阻止將諸如%foo%
之類的標記解釋為環境變量引用(至少在不將參數更改為%foo^%
的情況下不會,這將保留^
)。
在您的特定情況下,由於您試圖echo
顯參數unquoted ,因此此類參數中嵌入的雙引號 ( "
) - 需要轉義為""
- 不被正確支持:它們作為""
傳遞。
將不帶引號的參數傳遞給cmd.exe
/ 如果該參數包含cmd.exe
的元字符之一,則批處理文件會中斷,即具有特殊語法含義的字符; 在這種情況下,它們是: & | < > ^ "
& | < > ^ "
解決方案是將參數用雙引號 ( "..."
) 括起來,同時還需要將嵌入的"
字符加倍(值的一部分)。
PowerShell 在執行自己的命令行解析(特別是評估變量引用和表達式)之后,在幕后構造最終用於調用外部目標程序的命令行。
但是,它只會在參數包含空格時自動雙引號,而不是僅包含cmd.exe
元字符時; 例如,具有逐字字符串內容two (2)
的變量通過雙引號傳遞 - $val = 'two 2'; .\foo.bat $val
$val = 'two 2'; .\foo.bat $val
導致命令行.\foo.bat "two 2"
- 而字符串內容a&b
不是- $val = 'a&b'.\foo.bat $val
導致.\foo.bat a&b
-這打破了。
解決方案 - 如您的問題所示 - 是將變量引用括在文字中,嵌入的"
字符,因為這樣的“預引用”值指示 PowerShell 按原樣傳遞值:
$val = 'a&b'; .\foo.bat `"$val`"
$val = 'a&b'; .\foo.bat `"$val`"
結果為.\foo.bat "a&b"
注意: .\foo.bat "`"$val`""
效果相同; 我利用了 PowerShell 在參數(解析)模式下(通常)隱含地將 arguments 視為雙引號的事實; 在表達式(解析)模式中,例如在問題中的數組構造語句( @(..., ...)
)中,您確實需要"`"$val`""
形式。
正確的"..."
封閉參數(任何嵌入的"
字符。轉義為""
)被正確地視為批處理文件中的參數(例如, %1
)。
但是,它帶有封閉的雙引號和任何雙倍嵌入的"
字符。
如果您將此參數作為參數傳遞給目標程序(在這種情況下為python
),那么一切都會按預期工作。
但是,由於您通過 stdin使用echo
傳遞值,因此您需要去掉封閉的雙引號,以便它們不會作為value 的一部分傳遞,這是您的批處理文件嘗試的(例如, %~2
)
但是,傳遞剝離的值會導致echo
命令中斷。
echo
沒有很好的解決這個問題的方法,沒有執行繁瑣的顯式^
-轉義( ^
是cmd.exe
的轉義字符):
$escapedUnsecurePassword = $unsecurePassword -replace '[&|<>^]' -replace '"', '""'
& $script 'userA' `"$escapedUnsecurePassword`"
但是,僅此還不夠 - 您的batch_script.bat
文件也需要修改:因為您的SET password=%~2
命令中的賦值本身不受雙引號保護,它會與包含元字符的值中斷; 有點自相矛盾的是,您必須使用SET "password=%~2"
形式才能安全地去除嵌入的封閉"
字符。:
@echo off
REM Strip the enclosing "..." from the arguments (%~<n>)
REM !! The *assignment itself* must be in "..." so that
REM !! it does not break if the value has cmd.exe metacharacters.
set "username=%~1"
set "password=%~2"
echo(%password%|python main.py
請注意,這將按預期對所有元字符起作用,除了- 必須加倍- 嵌入的"
,它們作為""
傳遞。
但是,有一種解決方法可以用未引用的元字符來回顯字符串,正如subcoder 的有用答案中所展示的那樣:
如果你定義batch_script.bat
如下:
@echo off
set "username=%~1"
REM Do NOT strip quotes from the password argument
set password=%2
REM Using a trick with set /p, echo the password unquoted.
REM Note: Put the "|" directly after the ":" to avoid trailing spaces.
<NUL set /p=%password% & echo:|python main.py
變通方法重新調整了set
的/p
選項的用途,該選項在以交互方式提示用戶輸入值時接受要打印的提示消息,並打印不帶引號的消息; 實際的交互式提示通過<NUL
被抑制,因此只打印消息。
echo:
打印一個換行符(換行符),這是必要的,因為set /p
命令打印其消息時沒有尾隨換行符(如果您不想發送換行符,只需省略& echo:
)。
警告:除了嵌入的""
問題也在這里應用之外,這種解決方法還有一個副作用:它會修剪前導空格; 例如, " foo "
導致 output foo
(僅保留尾隨空格); 但是,鑒於帶有前導空格的 arguments 很少見,這在實踐中可能無關緊要。
鑒於上述方法的繁瑣/晦澀,頂部顯示的基於標准輸入的方法更可取。
您使用start
/ saps
的-argumentlist
開關將 arguments 傳遞給 powershell 中的批處理文件。 對你來說,你可以使用:
saps "$script" -argumentlist $args
但我建議首先分解$args
,因為它可能不起作用,因為通過 arguments,您通常希望一次通過 arguments,例如:
saps "$script" -argumentlist "1","2","3"
傳遞$args
在大多數情況下都會起作用,但在某些情況下它不起作用。 大多數時候你都很好
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.