powershell - 文件夹中每个文件中的 Powershell 更新字符串的问题
问题描述
我有一组存储在文件夹中的 SQL 文件。这些文件包含格式名称,dbo_xxxxxx
其中 xxxxxx 是年和月,例如dbo_202001
等dbo_202002
。我希望 powershell 脚本在每个 SQL 文件中用新的数字替换 xxxxx 数字。我正在使用下面的脚本来实现这一点。然而,问题在于它似乎在旧字符串上部分匹配(而不是在完整字符串上匹配)并将新字符串放在适当的位置,例如,而不是替换[dbo_202001]
为[dbo_201902]
,它找到的任何地方d
,b
等等o
。它替换为[dbo_201902]
。有任何解决这个问题的方法吗?
$sourceDir = "C:\SQL_Scripts"
$SQLScripts = Get-ChildItem $sourceDir *.sql -rec
foreach ($file in $SQLScripts)
{
(Get-Content $file.PSPath) |
Foreach-Object { $_ -replace "[dbo_202001]", "[dbo_201902]" } |
Set-Content $file.PSPath -NoNewline
}
解决方案
vonPryz和marsze提供了关键指针:由于-replace
运算符对正则表达式(正则表达式)进行操作,因此您必须\
-escape 特殊字符,例如[
and]
以便逐字处理它们(作为文字):
$_ -replace '\[dbo_202001\]', '[dbo_201902]'
虽然-replace
通常最好使用运算符,但该[string]
类型的.Replace()
方法直接提供逐字(文字)字符串替换,因此也比-replace
.
通常,这无关紧要,但在与您类似的情况下,涉及许多迭代,它可能(请注意,替换区分大小写):
$_.Replace('[dbo_202001]', '[dbo_201902]')
有关何时使用与. _-replace
.Replace()
您的代码的性能可以大大提高:
$sourceDir = 'C:\SQL_Scripts'
foreach ($file in Get-ChildItem -File $sourceDir -Filter *.sql -Recurse)
{
# CAVEAT: This overwrites the files in-place.
Set-Content -NoNewLine $file.PSPath -Value `
(Get-Content -Raw $file.PSPath).Replace('[dbo_202001]', '[dbo_201902]')
}
由于您无论如何都要将整个文件读入内存,因此使用
Get-Content
's-Raw
开关将其作为单个多行字符串(而不是行数组)读取,您可以在其上执行单个.Replace()
操作要快得多。Set-Content
的-NoNewLine
开关是为了防止在写回文件时附加一个额外的换行符。注意使用
-Value
参数而不是管道来提供文件内容。因为这里只有一个字符串对象要写,所以差别不大,但总的来说,有很多对象要写,这些对象已经收集在内存中,Set-Content ... -Value $array
比$array | Set-Content ...
.
-replace
操作员与.Replace()
方法的使用指南:
-replace
是基于 PowerShell 的正则表达式(正则表达式)的字符串替换运算符,- 而是.NET类型
.Replace()
的方法。_ ( ),它执行逐字(文字)字符串替换。[string]
System.String
请注意,这两个功能总是替换它们找到的所有匹配项,相反,如果没有找到,则返回原始字符串。
通常,PowerShell 的-replace
运算符更自然地适合 PowerShell 代码 - 无论是在语法上还是由于其不区分大小写 - 并且由于基于正则表达式而提供了更多功能。
该.Replace()
方法仅限于逐字替换,在 Windows PowerShell 中仅限于区分大小写的方法,但具有速度更快且不必担心在其参数中转义特殊字符的优点:
仅使用
[string]
类型的.Replace()
方法:- 用于始终逐字替换的字符串
- 具有以下区分大小写:
PowerShell [Core] v6+:默认区分大小写,通过附加参数可选择不区分大小写;例如:
'FOO'.Replace('o', '@', 'InvariantCultureIgnoreCase')
Windows PowerShell:总是(!)区分大小写
- 如果在功能上可行,当性能很重要时
否则,请使用 PowerShell 的
-replace
运算符(在此处详细介绍):对于基于正则表达式的替换:
支持复杂的、基于模式的匹配和替换字符串的动态构造
转义元字符(具有特殊句法含义的字符)以便逐字处理:
- 在模式(正则表达式)参数中:-
\
转义它们(例如,\.
或\[
) - 在替换参数中: only
$
是特殊的,将其转义为$$
.
- 在模式(正则表达式)参数中:-
转义整个操作数以逐字处理其值(有效地执行文字替换):
在模式参数中:调用
[regex]::Escape($pattern)
在替换参数中:调用
$replacement.Replace('$', '$$')
具有以下区分大小写:
- 默认情况下不区分大小写
- 可选地通过其-prefixed 变体区分大小写,
c
-creplace
注意:
-replace
是一个对 PowerShell 友好的封装[regex]::Replace()
方法,它不会暴露后者的所有功能,尤其是它限制替换次数的能力;请参阅此答案以了解如何使用它。
请注意,-replace
可以像 LHS 一样直接对字符串的数组(集合)进行操作,在这种情况下,对每个元素执行替换,这是按需字符串化的。
由于 PowerShell 的基本成员枚举功能,.Replace()
也可以对数组进行操作,但前提是所有元素都已经是字符串。此外,与如果 LHS 为 1-replace
也总是返回数组不同,如果输入对象恰好是单元素数组,则成员枚举返回单个字符串。
顺便说一句:类似的考虑适用于使用 PowerShell 的-split
运算符与[string]
类型的.Split()
方法 - 请参阅此答案。
例子:
-replace
-有关语法详细信息,请参阅此答案:
# Case-insensitive replacement.
# Pattern operand (regex) happens to be a verbatim string.
PS> 'foo' -replace 'O', '@'
f@@
# Case-sensitive replacement, with -creplace
PS> 'fOo' -creplace 'O', '@'
f@o
# Regex-based replacement with verbatim replacement:
# metacharacter '$' constrains the matching to the *end*
PS> 'foo' -replace 'o$', '@'
fo@
# Regex-based replacement with dynamic replacement:
# '$&' refers to what the regex matched
PS> 'foo' -replace 'o$', '>>$&<<'
fo>>o<<
# PowerShell [Core] only:
# Dynamic replacement based on a script block.
PS> 'A1' -replace '\d', { [int] $_.Value + 1 }
A2
# Array operation, with elements stringified on demand:
PS> 1..3 -replace '^', '0'
01
02
03
# Escape a regex metachar. to be treated verbatim.
PS> 'You owe me $20' -replace '\$20', '20 dollars'
You owe me 20 dollars.
# Ditto, via a variable and [regex]::Escape()
PS> $var = '$20'; 'You owe me $20' -replace [regex]::Escape($var), '20 dollars'
You owe me 20 dollars.
# Escape a '$' in the replacement operand so that it is always treated verbatim:
PS> 'You owe me 20 dollars' -replace '20 dollars', '$$20'
You owe me $20
# Ditto, via a variable and [regex]::Escape()
PS> $var = '$20'; 'You owe me 20 dollars' -replace '20 dollars', $var.Replace('$', '$$')
You owe me $20.
.Replace()
:
# Verbatim, case-sensitive replacement.
PS> 'foo'.Replace('o', '@')
f@@
# No effect, because matching is case-sensitive.
PS> 'foo'.Replace('O', '@')
foo
# PowerShell [Core] v6+ only: opt-in to case-INsensitivity:
PS> 'FOO'.Replace('o', '@', 'InvariantCultureIgnoreCase')
F@@
# Operation on an array, thanks to member enumeration:
# Returns a 2 -element array in this case.
PS> ('foo', 'goo').Replace('o', '@')
f@@
g@@
# !! Fails, because not all array elements are *strings*:
PS> ('foo', 42).Replace('o', '@')
... Method invocation failed because [System.Int32] does not contain a method named 'Replace'. ...
推荐阅读
- css - 为什么我的 Gatsby/Material-UI 站点呈现的比视口略大?
- azure - 在 azure ml 管道中,使用大型数据集训练模型时出错
- bash - 如何检查 bash 脚本的参数是否为“标志”
- ios - Ionic:不再接受 iOS UIWebView
- c# - 有谁知道为什么 C# 语音识别突然不再起作用了?
- python - 如何提高迭代的性能
- sass - Hugo loveit 主题中的样式定制
- ansible - ansible:在when语句中访问最后一个库存列表项/列表大小
- python-3.x - 如何在烧瓶管理页面中以人类可读文本显示关系数据
- flutter - 创建自定义许可页面