PowerShell写入含转义字符字符串到文件时转义序列丢失如何解决
动态生成PowerShell脚本时转义字符丢失问题
问题复现
项目开发中需要动态生成PowerShell脚本写入文件后供后续执行,过程中出现转义字符意外丢失的问题,最小复现流程如下:
场景1:直接输出特殊字符字符串(运行正常)
# 待写入的包含多数特殊字符的字符串 $_output_str = "~@#$%&*()-_=+{}[]<>?/.,'`"``;!" # 直接输出到标准输出 Write-Output "$_output_str"
运行结果符合预期,输出内容为:
~@#$%&*()-_=+{}[]<>?/.,'"`;!
场景2:拼接命令写入文件后执行(抛出解析错误)
尝试将输出命令拼接为字符串后写入脚本文件,代码如下:
# 待写入的包含多数特殊字符的字符串 $_output_str = "~@#$%&*()-_=+{}[]<>?/.,'`"``;!" # 拼接为待执行的命令字符串 $_cmd = "Write-Output `"$_output_str`"" # 将命令写入脚本文件 "$_cmd" | Out-File "execution.ps1"
执行生成的execution.ps1脚本:
powershell.exe .\execution.ps1
此时抛出如下解析错误:
At C:\{MYPATH}\execution.ps1:1 char:43 + Write-Output "~@#$%&*()-_=+{}[]<>?/.,'"`;!" + ~ The string is missing the terminator: ". + CategoryInfo : ParserError: (:) [], ParseException + FullyQualifiedErrorId : TerminatorExpectedAtEndOfString
查看生成的execution.ps1文件,实际写入的命令内容为:
Write-Output "~@#$%&*()-_=+{}[]<>?/.,'"`;!
问题根因:创建$_cmd字符串的过程中,原有转义序列被PowerShell自动解析移除,导致生成的脚本存在字符串未闭合的语法错误。
问题原因
PowerShell中双引号包裹的字符串会自动执行两类解析:
- 替换
$开头的变量为实际值 - 解析反引号(
`)开头的转义序列
拼接$_cmd时,$_output_str里原本用于转义双引号、反引号的转义符没有做二次转义,在$_cmd赋值阶段就被PowerShell解析消耗,最终写入文件的内容丢失了必要的转义字符,触发语法错误。
解决方案
方案1:调用官方代码生成API自动转义(推荐,无漏判风险)
PowerShell内置了代码生成相关的API,可以自动将任意字符串转换为可直接嵌入脚本的合法字面量,不需要手动处理任何转义规则:
# 原始特殊字符字符串 $_output_str = "~@#$%&*()-_=+{}[]<>?/.,'`"``;!" # 自动转义为单引号字符串的合法内容(仅会处理需要转义的单引号) $_escaped_content = [System.Management.Automation.Language.CodeGeneration]::EscapeSingleQuotedStringContent($_output_str) # 拼接命令,单引号包裹字符串字面量,无额外转义开销 $_cmd = "Write-Output '$_escaped_content'" # 写入脚本文件,显式指定UTF8编码避免跨版本乱码 $_cmd | Out-File -FilePath "execution.ps1" -Encoding utf8
该方法生成的脚本完全符合PowerShell语法,执行后输出结果和直接输出的结果完全一致,不会出现转义丢失问题,也能避免脚本注入风险。
说明:PowerShell单引号字符串为纯字面量字符串,除单引号本身需要用两个连续单引号转义外,其余所有特殊字符(包括$、反引号、双引号)都不会被解析,是动态拼接脚本时优先选择的字符串包裹方式。
方案2:手动转义(适合简单场景)
如果不想调用内置API,手动拼接时优先选择单引号作为目标脚本里的字符串包裹符,仅需要将原字符串里的单引号替换为两个连续单引号即可:
$_output_str = "~@#$%&*()-_=+{}[]<>?/.,'`"``;!" # 手动转义单引号 $_escaped_content = $_output_str.Replace("'", "''") $_cmd = "Write-Output '$_escaped_content'" $_cmd | Out-File -FilePath "execution.ps1" -Encoding utf8
方案3:使用单引号Here-String拼接长脚本
如果要生成的脚本内容较长,可以使用@' '@包裹的单引号here-string,内部所有内容都会按纯字面量处理,不会触发任何转义解析,仅需要提前处理好转义插入的变量即可:
$_output_str = "~@#$%&*()-_=+{}[]<>?/.,'`"``;!" $_escaped_content = $_output_str.Replace("'", "''") # 单引号here-string,内部内容无解析逻辑 $_cmd = @' Write-Output '{0}' '@ -f $_escaped_content $_cmd | Out-File -FilePath "execution.ps1" -Encoding utf8
避坑提示
- 禁止直接将未做转义的特殊字符串、外部输入内容直接拼接进双引号包裹的命令字符串,不仅会出现转义丢失问题,还存在脚本注入风险
- PowerShell 5.1版本
Out-File默认编码为UTF-16LE,跨场景执行容易出现乱码,写入文件时建议显式指定-Encoding utf8参数 - 动态生成脚本时优先选择单引号作为字符串包裹符,转义规则远简单于双引号字符串
内容的提问来源于stack exchange,提问作者Jordan
相关产品推荐
相关产品推荐

