在多个文件中搜索字符串,然后使用该字符串

时间:2017-01-30 23:01:05

标签: powershell

我试图创建一个PowerShell脚本来搜索一系列.txt文件(确切地说是.hl7文件,但它们只是txt文件)并在这些文件中搜索以查看它们是否包含四位数。如果文件确实包含该四位数字,则应使用添加到原始文件名前面的字符串重命名该文件。因此 test.hl7 应该变为 8000_test.hl7 ,如果该文件包含其中的4位数字。

经过一天凶猛的谷歌搜索和挖掘这个网站后,这是我能想到的最好的:

$AccountIDs = ("8155", "8156", "8428")
$Path = "C:\Users\ThatsMe\Downloads\messages"
$Files = (Get-ChildItem $Path -Filter "*.hl7")

for ($i = 0; $i -le $Files.Length; $i++) {
    if (Get-Content $Files[$i].FullName | Select-String -Pattern $AccountIDs[$i]) {
        Rename-Item $Files[$i].FullName -NewName (($AccountIDs[$i]) + "_" + $Files[$i].Name)
    }
}

我得到了一些有趣的结果。我目前在该消息文件夹中有四个测试文件,测试测试2 测试3 skibbidybop 。第一个测试正确地更改为 8156_test 。但是,其他文件没有被触及。现在,当我将测试的文件名更改为 ttest 时,脚本会完全跳过该文件,然后重命名 test2 test3 < / strong>分别为 8156_test2 (不正确)和 8428_test3 。永远不会触及 skibbidybop

当然,来自PowerShell的错误消息:

Select-String : Cannot bind argument to parameter 'Pattern' because it is null.
At line:6 char:61
+ if (Get-Content $Files[$i].FullName | Select-String -Pattern <<<<  $AccountIDs[$i]) {
    + CategoryInfo          : InvalidData: (:) [Select-String], ParameterBindingValidationException
    + FullyQualifiedErrorId : ParameterArgumentValidationErrorNullNotAllowed,Microsoft.PowerShell.Commands.SelectStringCommand

Get-Content : Cannot bind argument to parameter 'Path' because it is null.
At line:6 char:16
+ if (Get-Content <<<<  $Files[$i].FullName | Select-String -Pattern $AccountIDs[$i]) {
    + CategoryInfo          : InvalidData: (:) [Get-Content], ParameterBindingValidationException
    + FullyQualifiedErrorId : ParameterArgumentValidationErrorNullNotAllowed,Microsoft.PowerShell.Commands.GetContentCommand

更新代码

$Path = "C:\Users\ThatsMe\Downloads\messages"

$pattern = '\b(8155|8156|8428)\b'
Get-ChildItem $Path -Filter '*.hl7' |
    Select-String -Pattern $pattern |
    Group-Object Path |
    ForEach-Object {
        $id       = $_.Group.Matches[0].Groups[0].Value
        $filename = $_.Group.Filename | Select-Object -First 1
        Rename-Item -Path $_.Name -NewName "${id}_${filename}" -WhatIf
    }

这是我现在收到的错误:

C:\> C:\Users\ThatsMe\Downloads\messages\changename.ps1
Cannot index into a null array.
At C:\Users\ThatsMe\Downloads\messages\changename.ps1:8 char:38
+         $id       = $_.Group.Matches[ <<<< 0].Groups[0].Value
    + CategoryInfo          : InvalidOperation: (0:Int32) [], RuntimeException
    + FullyQualifiedErrorId : NullArray

What if: Performing operation "Rename File" on Target "Item:
C:\Users\ThatsMe\Downloads\messages\test.hl7 Destination:
C:\Users\ThatsMe\Downloads\messages\_".
Cannot index into a null array.
At C:\Users\ThatsMe\Downloads\messages\changename.ps1:8 char:38
+         $id       = $_.Group.Matches[ <<<< 0].Groups[0].Value
    + CategoryInfo          : InvalidOperation: (0:Int32) [], RuntimeException
    + FullyQualifiedErrorId : NullArray

What if: Performing operation "Rename File" on Target "Item:
C:\Users\ThatsMe\Downloads\messages\test3.hl7 Destination:
C:\Users\ThatsMe\Downloads\messages\_".

1 个答案:

答案 0 :(得分:3)

你得到的错误是由两个错误引起的,其中一个错误是经典off-by-one error。 PowerShell数组从零开始,这意味着数组的最后一个索引比其元素的数量少

[ 'a', 'b', 'c' ]   → count == 3
   0    1    2      → last index == 2 == 3-1

因此,当for 小于 $i$Files.Length),时,您的-lt循环可能会投放小于等于(-le):

for ($i = 0; $i -lt $Files.Length; $i++) {

此外,您不能对两个不同的数组($Files$AccountIDs)使用相同的索引变量,除非您确保两个数组的长度相同或至少与第二个数组相同({{1 }})具有比用于确定最大索引($AccountIDs)的元素更多的元素。如果$Files的元素少于$AccountIDs,则您的代码最终会尝试访问超出$Files上限的索引。此外,您可能想要检查每个文件中是否有来自$AccountIDs的所有数字。这样做需要一个带有第二个索引变量的嵌套循环。

话虽如此,你要让它变得比它需要的更复杂。您可以简单地将您的ID放在一个regular expression中,并将文件列表导入$AccountIDs,以针对该正则表达式检查它们:

Select-String

正则表达式$pattern = '\b(8155|8156|8428)\b' Get-ChildItem $Path -Filter '*.hl7' | Select-String -Pattern $pattern | Group-Object Path | ForEach-Object { $id = $_.Group.Matches[0].Groups[0].Value $filename = $_.Group.Filename | Select-Object -First 1 Rename-Item -Path $_.Name -NewName "${id}_${filename}" -WhatIf } 匹配任何给定的数字。 \b(8155|8156|8428)\b将匹配限制为字边界,以避免匹配81552或842893等数字。

\b语句可确保重命名文件的唯一性(这样,如果在文件中找到多个匹配项,则不会尝试多次重命名文件)。

Group-Object提取每个文件的第一个匹配的第一个捕获组的值。

.Matches[0].Groups[0].Value确保即使在文件中找到多个匹配项,您只有一个带有文件名的字符串,而不是它们的数组。

一旦确认重命名操作正常工作,就删除Select-Object -First 1开关,然后重新运行整个语句以实际重命名文件。

编辑:对于PowerShell v2,您需要稍微调整组处理,因为该版本不支持member enumeration

-WhatIf