使用 Select-String 在 powershell 中检查两个 .txt 文件
Using Select-String for checking two .txt files in powershell
我对编写 powershell 脚本完全陌生。到目前为止,我一直在使用普通批处理,因为这是我公司的要求。在此批次中,我使用嵌套的 foor 循环来比较两个 .txt 文件,具体我想执行以下操作:
- 文件 1 包含很多字符串。每个字符串都在一个单独的行中,前面有数字和分号,如下所示:
658;RMS
- 文件 2 是一些长文本。
目的是计算文件1中每个字符串在文件2中出现的次数,例如RMS 被计数 300 次。
由于我以前的代码在 运行 时间方面有一些巨大的缺点(文件 1 有大约 400 行,文件 2 有 500.000 行)我读到 Powershell 的 Select-String 效率更高.
但是,当我正在阅读一些教程时,我不清楚如何在这里继续,除此之外我必须 运行 我的 .bat 中的 powershellcode。
我最大的问题是我不确定如何以及在哪里放置我的 'variables',所以两个输入文件 1 和 2
到目前为止,我正在测试 Select-String 方法,如下所示:
powershell -command "& {Select-String -Path *.txt -Pattern "RMS"}"
我的假设是使用管道,所以像这样:
powershell -command "& {<<path to file one, should read line by line>> | Select-String -Path File2.txt -Pattern "value of file 1"}"
但是,我没有让它工作。 Powershell 在第一个管道之前期待某种 psobject
?
Select-String
有用,但它并不神奇:)
考虑到性能影响,我会这样处理:
- 对于
File2
中的每一行:
- 测试所有项在
File1
中的出现
这样,你只需要读取和计算File2
一次:
# prepare hashtable to keep track of count
$count = @{}
# read terms to search for from file1
$termsToFind = Get-Content .\file1 |ForEach-Object {
$_ -split ';' |Select -Last 1
}
# loop over lines in file2, count the words we're searching for
Get-Content .\test\file2 |ForEach-Object {
foreach($term in $termsToFind){
# Using `Regex.Matches()` will help us find multiple occurrences of the same term
$count[$term] += [regex]::Matches($_,"\b$([regex]::Escape($term))\b").Count
}
}
现在 $count
将是一个哈希表,其中键是文件 1 中的术语,值是每个单词的计数。
输出格式与 file1
相同:
$count.GetEnumerator() |ForEach-Object { $_.Value,$_.Key -join ';' } |Set-Content output.txt
如果你查看文档,你不能将 -pattern 传递给 select-string。您可以使用括号使某些内容的输出成为模式参数:
powershell select-string -pattern (get-content file1) -path file2
利用 pattern 是位置 0 和 path 是位置 1 的事实。-pattern 也可以是数组。
powershell select-string (get-content file1) file2
为了获得最佳性能,我会像这样处理此任务。
- 读取包含条款的 CSV 文件(它 是 CSV,带有
;
分隔符)
- 将另一个文件读入字符串
- 对于每个术语,计算它在目标字符串中出现的频率(使用
.IndexOf()
)
例如
$data = Import-Csv "file1.txt" -Delimiter ";" -Header ID,Term
$target = Get-Content "file2.txt" -Raw
$counts = @{}
foreach ($term in $data.Term) {
$index = -1
$count = 0
do {
$index = $target.IndexOf($term, $index + 1)
if ($index -gt -1) { $count++ } else { break; }
} while ($true);
$counts[$term] = $count
}
$counts
备注
Import-Csv
将自动使用输入文件中的第一行作为 header。如果您的文件已经有 header,您可以删除 -Headers
参数。
Get-Content
将默认将输入文件读入行数组。但是对于这种方法,将整个文件作为一个大字符串是正确的 - 这就是 -Raw
所做的。
@{}
创建一个空哈希表
$data.Term
将访问 CSV 的一列
.IndexOf()
区分大小写。默认情况下,PowerShell 是 case-insenstive,但像这样的原生 .NET 方法不会改变它们的行为。这可能是也可能不是您需要的 - 如果您不关心大小写,请在 $target
和 $term
上使用 .ToLower()
。
我对编写 powershell 脚本完全陌生。到目前为止,我一直在使用普通批处理,因为这是我公司的要求。在此批次中,我使用嵌套的 foor 循环来比较两个 .txt 文件,具体我想执行以下操作:
- 文件 1 包含很多字符串。每个字符串都在一个单独的行中,前面有数字和分号,如下所示:
658;RMS
- 文件 2 是一些长文本。
目的是计算文件1中每个字符串在文件2中出现的次数,例如RMS 被计数 300 次。
由于我以前的代码在 运行 时间方面有一些巨大的缺点(文件 1 有大约 400 行,文件 2 有 500.000 行)我读到 Powershell 的 Select-String 效率更高. 但是,当我正在阅读一些教程时,我不清楚如何在这里继续,除此之外我必须 运行 我的 .bat 中的 powershellcode。 我最大的问题是我不确定如何以及在哪里放置我的 'variables',所以两个输入文件 1 和 2
到目前为止,我正在测试 Select-String 方法,如下所示:
powershell -command "& {Select-String -Path *.txt -Pattern "RMS"}"
我的假设是使用管道,所以像这样:
powershell -command "& {<<path to file one, should read line by line>> | Select-String -Path File2.txt -Pattern "value of file 1"}"
但是,我没有让它工作。 Powershell 在第一个管道之前期待某种 psobject
?
Select-String
有用,但它并不神奇:)
考虑到性能影响,我会这样处理:
- 对于
File2
中的每一行:- 测试所有项在
File1
中的出现
- 测试所有项在
这样,你只需要读取和计算File2
一次:
# prepare hashtable to keep track of count
$count = @{}
# read terms to search for from file1
$termsToFind = Get-Content .\file1 |ForEach-Object {
$_ -split ';' |Select -Last 1
}
# loop over lines in file2, count the words we're searching for
Get-Content .\test\file2 |ForEach-Object {
foreach($term in $termsToFind){
# Using `Regex.Matches()` will help us find multiple occurrences of the same term
$count[$term] += [regex]::Matches($_,"\b$([regex]::Escape($term))\b").Count
}
}
现在 $count
将是一个哈希表,其中键是文件 1 中的术语,值是每个单词的计数。
输出格式与 file1
相同:
$count.GetEnumerator() |ForEach-Object { $_.Value,$_.Key -join ';' } |Set-Content output.txt
如果你查看文档,你不能将 -pattern 传递给 select-string。您可以使用括号使某些内容的输出成为模式参数:
powershell select-string -pattern (get-content file1) -path file2
利用 pattern 是位置 0 和 path 是位置 1 的事实。-pattern 也可以是数组。
powershell select-string (get-content file1) file2
为了获得最佳性能,我会像这样处理此任务。
- 读取包含条款的 CSV 文件(它 是 CSV,带有
;
分隔符) - 将另一个文件读入字符串
- 对于每个术语,计算它在目标字符串中出现的频率(使用
.IndexOf()
)
例如
$data = Import-Csv "file1.txt" -Delimiter ";" -Header ID,Term
$target = Get-Content "file2.txt" -Raw
$counts = @{}
foreach ($term in $data.Term) {
$index = -1
$count = 0
do {
$index = $target.IndexOf($term, $index + 1)
if ($index -gt -1) { $count++ } else { break; }
} while ($true);
$counts[$term] = $count
}
$counts
备注
Import-Csv
将自动使用输入文件中的第一行作为 header。如果您的文件已经有 header,您可以删除-Headers
参数。Get-Content
将默认将输入文件读入行数组。但是对于这种方法,将整个文件作为一个大字符串是正确的 - 这就是-Raw
所做的。@{}
创建一个空哈希表$data.Term
将访问 CSV 的一列.IndexOf()
区分大小写。默认情况下,PowerShell 是 case-insenstive,但像这样的原生 .NET 方法不会改变它们的行为。这可能是也可能不是您需要的 - 如果您不关心大小写,请在$target
和$term
上使用.ToLower()
。