我正在使用 shell 读取 Redis 转储文件。
转储文件中有 3 个主要列,如下所示。
Text:tags:name 682651 520
Text:tags:age 78262 450
Value:cache 77272 672
Value:cache:name 76258 872
New:specific 77628 762
New:test 76628 8622
预期输出:
Key Count Sum
Text:* 2 970
Value:* 2 1544
New:* 2 9384
希望获得上述预期,因为可以根据子字符串检查列,可能会以字符串(键)开头/中间/结尾。
答案1
以下awk
程序将执行该任务:
awk '{split($1,f,/:/);count[f[1]]++;sum[f[1]]+=$3}
END{printf "Key\tCount\tSum\n"; for (k in count) {printf "%s:*\t%d\t%d\n",k,count[k],sum[k]}}' dump.txt
- 这将首先将第 1 列的键拆分为
:
多个组件,然后将其存储在数组中f
。第一个条目 (f[1]
) 被视为所有进一步处理的相关键。 - 出现次数将存储在一个关联数组中
count
,以键f[1]
作为数组索引。每次找到密钥时它都会加 1。 - 第 3 列中的值的总和类似地存储在关联数组中
sum
。 - 最后,程序打印标题行,然后迭代数组中找到的所有数组索引,
count
以打印数组索引(= 键)、出现次数和求和值。
请注意,打印键的顺序是由awk
存储数组的内部逻辑定义的。如果您有 GNU Awk,则可以PROCINFO["sorted_in"]
在BEGIN
节中设置属性来定义遍历顺序。例如
BEGIN{PROCINFO["sorted_in"]="@ind_str_asc"}
将使awk
按“键”的字典顺序升序打印条目。
答案2
首先修改第一个字段,以便第一个字段之后的所有内容:
都替换为*
:
awk -v OFS='\t' '{ sub(":.*", ":*", $1) }; 1' file
此命令通过将扩展正则表达式的第一个匹配项替换为文字字符串 来awk
修改第一个空格分隔字段。根据您的数据,其输出将是以下制表符分隔的数据::.*
:*
Text:* 682651 520
Text:* 78262 450
Value:* 77272 672
Value:* 76258 872
New:* 77628 762
New:* 76628 8622
然后,我们可以按第一个制表符分隔字段对其进行分组,同时计算每组中折叠的行数并对第三个字段求和。一种方法是使用 GNU datamash
:
awk -v OFS='\t' '{ sub(":.*", ":*", $1) }; 1' file |
datamash groupby 1 count 1 sum 3
awk
如果原始输入未排序,则要么传递数据sort
,要么使用datamash
其-s
选项。
这将输出以下内容(以制表符分隔):
Text:* 2 970
Value:* 2 1544
New:* 2 9384
要输出标头,只需首先调用
printf '%s\t%s\t%s\n' 'Key' 'Count' 'Sum'
完整的事情,读取file
和写入一些新文件output
:
{
printf '%s\t%s\t%s\n' 'Key' 'Count' 'Sum'
awk -v OFS='\t' '{ sub(":.*", ":*", $1) }; 1' |
datamash groupby 1 count 1 sum 3
} <file >output
答案3
$ awk -F'[:[:space:]]+' '
{ cnt[$1]++; sum[$1]+=$NF }
END {
print "Key", "Count", "Sum"
for (key in cnt) {
print key":*", cnt[key], sum[key]
}
}
' file | column -t
Key Count Sum
Value:* 2 1544
Text:* 2 970
New:* 2 9384
答案4
使用乐(以前称为 Perl_6)
~$ raku -e 'my (@k, @v); given lines.map(*.words).list { \
@k = .map(*.[0].split(":").[0]); @v = .map(*.[2]) }; \
my %count1 = Bag(@k); my %agg1.=append: [Z=>] @k, @v; \
my %sum1; for %agg1 { %sum1.append: $_.key => [+] $_.value }; \
for ([Z] %count1.sort, %sum1.sort) { \
say .[0].key ~qb[\t]~ .[0].value ~qb[\t]~ .[1].value};' file
输入示例:
Text:tags:name 682651 520
Text:tags:age 78262 450
Value:cache 77272 672
Value:cache:name 76258 872
New:specific 77628 762
New:test 76628 8622
示例输出:
New 2 9384
Text 2 970
Value 2 1544
该解决方案利用了所有 Perl 系列语言中存在的哈希功能,这里专门用 Raku 编写。
简而言之,lines
被读入,并且每一行被分成以空格分隔的words
(map
这是指示 Raku 在“每个”行元素内工作的内容)。数据被保存到包含冒号@k
后第一个字符串元素的数组中,以及仅包含第三列(零索引 = 2)的数组中。此时数组看起来像这样:split
@v
#Add this statement:
.say for @k, @v;
#Returns:
[Text Text Value Value New New]
[520 450 672 872 762 8622]
Raku 有一个Bag()
内置函数,它给出每个键的出现次数(计数)(使用行my %count1 = Bag(@k);
)。
从这里%agg1
创建一个聚合哈希,它“压缩”@k
和@v
列,=>
将它们配对在一起,并将append
它们放入哈希中%agg1
。当然,散列的一个属性是每个key
都是唯一的,因此values
与相同的散列相关联key
被放置为构成每个键的数组元素value
。此时%agg1
哈希看起来像这样:
#Add this statement:
.say for %agg1.sort;
#Returns:
New => [762 8622]
Text => [520 450]
Value => [672 872]
%agg1
然后再次迭代该哈希以创建哈希,该哈希(您猜对了)以明智的方式%sum1
对个体求和。最后,输出数据,用制表符分隔(引号制表符减少了代码中对双引号的需要),并通过波形符完成字符串连接。values
key
~qb[\t]~
\t
~
https://andrewshitov.com/2020/03/16/a- Couple-of-syntax-sweets-in-raku/
https://youtu.be/wViVLytlwb8
https://raku.org