如何计算由 2 个字段分隔符(“:” 和 “,”)分隔的行中单列的字段数?

how to count number of fields for a single column in a row separated by 2 field separators (":" and ",")?

提问人:sirducas 提问时间:6/13/2022 更新时间:6/15/2022 访问量:498

问:

给定文本文件:
(结构为:“group_name:PW:group_id:User1<,User2>...”)

adm:x:4:syslog,adm1
admins:x:1006:adm2,adm12,manuel
ssl-cert:x:122:postgres
ala2:x:1009:aceto,salvemini
conda:x:1011:giovannelli,galise,aceto,caputo,haymele,salvemini,scala,adm2,adm12
adm1Group:x:1022:adm2,adm1,adm3
docker:x:998:manuel

如何计算每条线路的用户数?还是单行?

例如,如果我想知道有多少用户包含“adm1Group”,则输出应为 3,因为 adm1Group 有三个用户adm2、adm1 和 adm3)。另一个示例是第一行(组名“adm”),包含两个用户:syslogadm1

主要问题是这里有两个字段分隔符,那么我如何在同一个 AWK 命令中分隔 $4 列?我有这个解决方案,但在这里我使用了两个与管道链接的不同 awk 命令,如下所示(我不知道这对内核来说是否正确或“合法”):

awk -F: '/adm1Group/ {print $4}' file.txt | awk -F, 'BEGIN {printf "N. of users in adm1Group = "} {print NF}'

我可以在单个 AWK 命令中实现这样的解决方案吗?如果没有,我可以用这个吗?还是这个解决方案是“不良做法”?

评论

0赞 Dominique 6/13/2022
这个 StackOverflow 问题可能对您非常有用:stackoverflow.com/questions/12204192/...

答:

1赞 anubhava 6/13/2022 #1

您可以为此使用:split

awk -F: '$1 == "adm1Group" {print split($NF, a, /,/)}' file
3


awk -F: '$1 == "conda" {print split($NF, a, /,/)}' file
9

或者将它们全部打印在一起:

awk -F: '{print split($NF, a, /,/), "no of users in adm1Group:", $1}' file

2 no of users in adm1Group: adm
3 no of users in adm1Group: admins
1 no of users in adm1Group: ssl-cert
2 no of users in adm1Group: ala2
9 no of users in adm1Group: conda
3 no of users in adm1Group: adm1Group
1 no of users in adm1Group: docker

评论

0赞 sirducas 6/13/2022
conda 有 9 个用户,对吧?我只是在寻找拆分内置函数,因为我从不使用它
0赞 anubhava 6/13/2022
是的,当然。这是早些时候的错别字。如果成功了,请考虑接受答案。9
1赞 RavinderSingh13 6/13/2022 #2

使用您展示的示例和尝试,请尝试以下代码。这将打印Input_file的每个组名称中存在的用户总数。awk

awk -F':' '
{
  num=0
  arr1[$1]=num=split($NF,arr2,",")
}
END{
  for(i in arr1){
    print "Group " i " has " arr1[i] " users."
  }
}
' Input_file

解释:为上述代码添加详细说明。

awk -F':' '                          ##Starting awk program where setting field separator as : here.
{
  num=0                              ##Setting num as 0 here.
  arr1[$1]=num=split($NF,arr2,",")   ##Creating arr1 array with index of $1 and has value of num, which contains total number of total elements in arr2 with delimiter of , here.
}
END{                                 ##Starting END block of this program from here.
  for(i in arr1){                    ##Traversing through arr1 here.
    print "Group " i " has " arr1[i] " users."  ##printing group name and its value(how many times users came for that group).
  }
}
' Input_file                         ##Mentioning Input_file name here.

评论

0赞 sirducas 6/13/2022
是的,这很好用,但你能解释一下第一个 awk 块的第二行吗?就像一个“双重”赋值(arr=num=split(..)),我们可以将该行简化为两行吗?
1赞 RavinderSingh13 6/13/2022
@sirducas,是的,我将在一分钟左右的时间内添加详细的解释。
0赞 RavinderSingh13 6/13/2022
@sirducas,我已经为上面的代码添加了详细的解释,如有任何疑问,请告诉我。
0赞 sirducas 6/13/2022
那么,拆分函数总是返回一个整数,或者它只在这种情况下返回一个整数,因为我们使用的是$NF?
0赞 RavinderSingh13 6/13/2022
@sirducas,所以它是这样工作的:函数将最后一列($NF)拆分为数组 arr2,其中 $NF 中的分隔符是 和 是 arr2 数组的元素总数,或者在其他语言中,您可以说出用户的所有元素值在最后一个字段中分隔(num 包含那个),其中 (num) 进一步分配给 arr1 的值。如有任何疑问,请告诉我,欢呼。split($NF,arr2,","),num,
2赞 Daweo 6/13/2022 #3

如何计算每条线路的用户数?或单个 线?

我会用 GNU 来计算第 4 个字段内的数量,然后增加它,让内容为AWK,1file.txt

adm:x:4:syslog,adm1
admins:x:1006:adm2,adm12,manuel
ssl-cert:x:122:postgres
ala2:x:1009:aceto,salvemini
conda:x:1011:giovannelli,galise,aceto,caputo,haymele,salvemini,scala,adm2,adm12
adm1Group:x:1022:adm2,adm1,adm3
docker:x:998:manuel

然后

awk 'BEGIN{FS=":"}{printf "N of users in %s is %s\n", $1, gsub(/,/,"",$4)+1}' file.txt

给出输出

N of users in adm is 2
N of users in admins is 3
N of users in ssl-cert is 1
N of users in ala2 is 2
N of users in conda is 9
N of users in adm1Group is 3
N of users in docker is 1

解释:我告诉GNU字段分隔符()是。对于我使用的每一行,它的作用类似于填充模板和打印,对于填充,我使用第 1 个字段 () 和当命令使用空字符串 () 替换时完成的更改次数 gsub 函数在第 4 个字段 () 增加 1(因为姓氏没有尾随)。请注意,这确实会改变(删除字符),但对于此任务,所述副作用无关紧要。请注意,使用时需要隐式提供换行符 (),而不是 .AWKFS:printf$1,""$4,$4,printf\nprint

(在 Gawk 4.2.1 中测试)

评论

1赞 kvantour 6/13/2022
使用 gsub 的干净解决方案!但是,如果用户想要以原始形式再次重用,则可能需要将其替换为而不是替换。,""$4
0赞 sirducas 6/13/2022
是的,这是我的第一个想法作为解决方案,但是我遇到了没有用户的字符串的问题,即此命令应输出零的情况。(我知道这可能是不可能的,因为每个用户都被分配到一个组,但从理论上讲,其他非用户数据是可能的),在这种情况下,输出总是 1,因为 +1,对吧?还是我错了?
1赞 Daweo 6/13/2022
@sirducas 对于给定的空列号,是 ,但这可以使用三元运算符进行补偿,例如1{n=gsub(/,/,"",$4);printf "N of users in %s is %s\n", $1, n==0?0:n+1}
1赞 glenn jackman 6/13/2022 #4

使用 作为字段分隔符,然后打印字段数减去 3 个前导字段::,

awk -F'[:,]' '{print $1, NF - 3}' file
awk -F'[:,]' -v group=conda '$1 == group {print NF - 3}' file

评论

0赞 sirducas 6/13/2022
我喜欢这个解决方案及其工作原理,但是此命令仅限于此文件文本,具有这种特定结构,对吗?例如,如果我不知道名称组和用户列表之间的确切字段数,此命令是否仍然有效?
0赞 glenn jackman 6/14/2022
你是对的,硬编码是可变文件格式的问题。如果您知道逗号分隔字段是最后一个字段,请使用给定的解决方案之一。3split
0赞 RARE Kpop Manifesto 6/15/2022 #5
{m,g}awk '$!NF=sprintf("%20s\47s user(s) count = %\0478.f",$!_,NF-!_)' FS=':.+:|,'

          adm's  user(s) count =         2
       admins's  user(s) count =         3
     ssl-cert's  user(s) count =         1
         ala2's  user(s) count =         2
        conda's  user(s) count =         9
    adm1Group's  user(s) count =         3
       docker's  user(s) count =         1

稍加修改,现在完整的用户列表将在尾部提供。具体来说,小粗体项目 - 现在它正在覆盖而不是 ::$1$0

{m,g}awk ' $!_ = sprintf("%15s\47s user(s) count = %\0476.f",$!_,NF-!_)' FS=':.+:|,'

        adm's user(s) count =      2 syslog adm1
     admins's user(s) count =      3 adm2 adm12 manuel
   ssl-cert's user(s) count =      1 postgres
       ala2's user(s) count =      2 aceto salvemini
      conda's user(s) count =      9 giovannelli galise aceto caputo haymele salvemini scala adm2 adm12
  adm1Group's user(s) count =      3 adm2 adm1 adm3
     docker's user(s) count =      1 manuel

评论

0赞 jarno 12/5/2022
你为什么要写而不是而不是?!NF0!_1
0赞 RARE Kpop Manifesto 12/5/2022
@jarno : 因为有时只将数字分配给 时会采取行动,而分配给 过于冗长 - 是规避该问题的方法。mawk$0$1 = $0 = …$!NF
0赞 jarno 12/5/2022
哦,我猜这是一个错误,那么。mawk
0赞 RARE Kpop Manifesto 12/6/2022
@jarno : 实际上,所有人都有这个,实际上,通过设计:打印绝对没有,因为“模式”被评估为假,这要归功于那个 0。但是,根据需要打印 ,因为它是一个非空字符串,恰好包含数字“0”而不是数字。echo 'abc' | gawk '$_ = 0'echo 'abc' | gawk '$_ = "0"'"0"ASCII
0赞 RARE Kpop Manifesto 12/6/2022
@jarno :无论分配什么,保证打印的最安全方法是取整个表达式的 0 次方 - 它可以是正 INFinity、负 NaN 或一串 5 个表情符号,没关系 - 0 的幂总是产生 1awk