我在编写Powershell脚本方面完全是新手。到目前为止,我正在使用普通批次来实现我的目的,因为这是我公司的要求。在这个批次中,我使用嵌套的foor循环来比较两个.txt文件,详细地我想执行以下操作:
- 文件 1 包含大量字符串。每个字符串都在一行中,前面有一个数字和分号,如下所示:
658;RMS
- 文件 2 是一些长文本。
目的是计算文件 2 中文件 1 中每个字符串的出现次数,例如 RMS 计数 300 次。
由于我以前的代码在运行时有一些巨大的缺点(文件 1 大约有 400 行,文件 2 500.000(,我读到 Powershell 中的选择字符串效率更高。 但是,当我正在阅读一些教程时,我不清楚如何在这里继续,除此之外,我必须在.bat内运行 powershellcode。 我最大的问题是我不确定如何以及在哪里放置我的"变量",所以两个输入文件 1 和 2
到目前为止,我正在像这样测试选择字符串方法:
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
?
为了获得最佳性能,我会这样处理这项任务。
- 以 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
将自动使用输入文件中的第一行作为标头。如果文件已有标头,则可以删除-Headers
参数。- 默认情况下,
Get-Content
会将输入文件读取到行数组中。但是对于这种方法,将整个文件作为一个大字符串是正确的 - 这就是-Raw
所做的。 @{}
创建一个空哈希表$data.Term
将访问 CSV 的一列.IndexOf()
区分大小写。默认情况下,PowerShell 不区分大小写,但像这样的本机 .NET 方法不会更改其行为。这可能是您所需要的,也可能不是您需要的 - 如果您不关心大小写,请在$target
和$term
上使用.ToLower()
。
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 .testfile2 |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
将是一个哈希表,其中键是 file1 中的术语,值是每个单词的计数。
输出为与file1
相同的格式:
$count.GetEnumerator() |ForEach-Object { $_.Value,$_.Key -join ';' } |Set-Content output.txt
如果您检查文档,则无法通过管道模式将 -pattern 传输到选择字符串。 您可以使用括号使某些内容的输出成为模式参数:
powershell select-string -pattern (get-content file1) -path file2
利用模式是位置 0 和路径是位置 1 的事实。 -pattern 也可以是一个数组。
powershell select-string (get-content file1) file2