将行的第一部分与固定字符串匹配并计算第二部分中的唯一值
Match the first part of lines against a fixed string and count unique values in the second part
$var
包含数千行,格式如下:
./abc bbd xyh doc
./docs 2019 abc docx
./docs 2019 abc docx
./docs 2019 abc ppt
./docs 2019 abc ppt
./docs 2019 abc xls
./docs 2019 abc def docx
./docs 2019 abc/def docx
./bdg/aabc/dd efgh 2018 doc
. xls
. pptx
./aax bcd/def/gfhe ttp/five ppt
最后一列代表文件的 extension
,其他所有内容(从每行的开头,直到最后一个空白字符)是相应文件的 basename
(路径)。
有一个 while 循环为 $path
生成值,其中包含测试 basename
(路径),我的目标是从 $var
中删除所有不匹配的行 $path
从行首开始,直到最后一个空格(不包括最后一列)。此外,我只想打印相应的扩展名(如 | sort | uniq -c
)。
例如,如果在 while 循环的迭代期间我们发送 path="./docs 2019 abc"
,输出应该是实现以下目标的最快方式:
2 docx
2 ppt
1 xls
这就是我最终得到的结果,但输出是 错误的 - 它打印基本名称,而不是扩展名,并且每次迭代都非常慢:
printf "echo -e \"%s\" | awk '{$NF=\"\";} ( $0 ~ /%s/ )' | sort | uniq -c | sort -k1 -nr" "${var}" "${path//\//\/}" | bash
输出:
2 ./docs 2019 abc
2 ./docs 2019 abc
1 ./docs 2019 abc
$ path='./docs 2019 abc'
$ grep -Pox "\Q$path\E\s\K\S+" <<< ${var} | sort | uniq -c
2 docx
2 ppt
1 xls
这使用了 PCRE and thus requires GNU grep.
对于 GNU awk 它将是:
$ cat prog.awk
gensub(/\s\S+$/, "", 1) == path {
cnt[$NF]++
}
END {
PROCINFO["sorted_in"] = "@val_num_desc"
for (ext in cnt) {
print cnt[ext], ext
}
}
$ gawk -v path='./docs 2019 abc' -f prog.awk <<< ${var}
2 docx
2 ppt
1 xls
这种方法可能比前者更快,因为它不会生成 sort
和 uniq
。
以防万一 none 上述工具可用,这里有一个可移植的解决方案:
$ cat prog.awk
{
ext = $NF
sub(/[[:space:]][^[:space:]]+$/, "")
if ([=13=] == path)
cnt[ext]++
}
END {
for (ext in cnt)
print cnt[ext], ext
}
$ awk -v path='./docs 2019 abc' -f prog.awk <<< ${var} | sort -k1nr
2 docx
2 ppt
1 xls
请注意,所有这些都严重依赖于您对输入的描述,并且不会处理您可能错过的任何边缘情况。
$var
包含数千行,格式如下:
./abc bbd xyh doc
./docs 2019 abc docx
./docs 2019 abc docx
./docs 2019 abc ppt
./docs 2019 abc ppt
./docs 2019 abc xls
./docs 2019 abc def docx
./docs 2019 abc/def docx
./bdg/aabc/dd efgh 2018 doc
. xls
. pptx
./aax bcd/def/gfhe ttp/five ppt
最后一列代表文件的 extension
,其他所有内容(从每行的开头,直到最后一个空白字符)是相应文件的 basename
(路径)。
有一个 while 循环为 $path
生成值,其中包含测试 basename
(路径),我的目标是从 $var
中删除所有不匹配的行 $path
从行首开始,直到最后一个空格(不包括最后一列)。此外,我只想打印相应的扩展名(如 | sort | uniq -c
)。
例如,如果在 while 循环的迭代期间我们发送 path="./docs 2019 abc"
,输出应该是实现以下目标的最快方式:
2 docx
2 ppt
1 xls
这就是我最终得到的结果,但输出是 错误的 - 它打印基本名称,而不是扩展名,并且每次迭代都非常慢:
printf "echo -e \"%s\" | awk '{$NF=\"\";} ( $0 ~ /%s/ )' | sort | uniq -c | sort -k1 -nr" "${var}" "${path//\//\/}" | bash
输出:
2 ./docs 2019 abc
2 ./docs 2019 abc
1 ./docs 2019 abc
$ path='./docs 2019 abc'
$ grep -Pox "\Q$path\E\s\K\S+" <<< ${var} | sort | uniq -c
2 docx
2 ppt
1 xls
这使用了 PCRE and thus requires GNU grep.
对于 GNU awk 它将是:
$ cat prog.awk
gensub(/\s\S+$/, "", 1) == path {
cnt[$NF]++
}
END {
PROCINFO["sorted_in"] = "@val_num_desc"
for (ext in cnt) {
print cnt[ext], ext
}
}
$ gawk -v path='./docs 2019 abc' -f prog.awk <<< ${var}
2 docx
2 ppt
1 xls
这种方法可能比前者更快,因为它不会生成 sort
和 uniq
。
以防万一 none 上述工具可用,这里有一个可移植的解决方案:
$ cat prog.awk
{
ext = $NF
sub(/[[:space:]][^[:space:]]+$/, "")
if ([=13=] == path)
cnt[ext]++
}
END {
for (ext in cnt)
print cnt[ext], ext
}
$ awk -v path='./docs 2019 abc' -f prog.awk <<< ${var} | sort -k1nr
2 docx
2 ppt
1 xls
请注意,所有这些都严重依赖于您对输入的描述,并且不会处理您可能错过的任何边缘情况。