2016-09-15 69 views
1

我需要做的是从etc/group获取用户列表,然后对其进行排序,然后对唯一条目进行计数。对字符串数组中的唯一条目进行排序和计数

现在我只设法获取用户名。但我怀疑这是错误的。

#!/bin/bash 
usernames=(); 

while IFS=: read -r Groups Tmp1 Tmp2 Username 
do 
    if [ $Username!="" ]; 
    then 
    usernames+=($Username); 
    fi; 
done < /etc/group 

然后,我还试图对它进行排序,但输出非常怪异:

排序:

IFS=$'\n' sorted=($(sort <<<"${usernames[*]}")) 
unset IFS 

输出:

echo ${usernames[@]} 
echo "" 
echo ${sorted[@]} 

结果:

root root root root root root _teamsserver root root _taskgated root root,_jabber,_postfix,_cyrus,_calendar,_dovecot _calendar,_jabber,_postfix _devicemgr,_teamsserver _eppc root _teamsserver _devicemgr _softwareupdate _locationd _teamsserver _devicemgr,_calendar,_teamsserver,_xserverdocs _teamsserver,_devicemgr _warmd 

_calendar,_jabber,_postfix _devicemgr _devicemgr,_calendar,_teamsserver,_xserverdocs _devicemgr,_teamsserver _eppc _locationd _softwareupdate _taskgated _teamsserver _teamsserver _teamsserver _teamsserver,_devicemgr _warmd root root root root root root root root root root root,_jabber,_postfix,_cyrus,_calendar,_dovecot 

我有零bash的经验,绝对不能得到它的工作。

我需要什么最基本的解决方案来获取来自/ etc/group的用户名的排序列表,只有唯一的条目和打印每个的重复数量。

对于离,如果我有这个/etc/group文件:

nobody:*:-2: 
nogroup:*:-1: 
wheel:*:0:root 
daemon:*:1:root 
kmem:*:2:root 
sys:*:3:root 
tty:*:4:root 
operator:*:5:root 
mail:*:6:_teamsserver 

我想这一点:

root 6 
_teamsserver 1 
+0

请添加一个可验证的样本我/ p和预期o/p。你的'/ etc/group'文件和你的输出格式。 – Inian

+0

尝试'cut -d:-f1/etc/group |排序| uniq -c' – Sundeep

+0

刚刚添加了所需的示例 – s1ddok

回答

3

每一个“用户名”字段实际上是用户名的任意空逗号分隔的列表。为了分隔用户名,你需要用逗号分隔条目。

如果我从你的循环开始了,我可能会使用:

sorted=($(while IFS=: read -r Groups Tmp1 Tmp2 Usernames 
      do 
       if [ -n "$Usernames" ]; 
       then 
        echo "$Usernames" 
       fi 
      done < /etc/group | 
      tr ',' '\n' | 
      sort -u 
     )) 

echo "${sorted[@]}" 

这绕过了中间usernames阵列。如果你真的想要的,然后让你的原始循环和管道sort之前通过tr命令输入sort

IFS=$'\n' sorted=($(tr ',' '\n' <<<"${usernames[*]}" | sort -u)) 

这会生成一个数组,sorted,包含排序顺序的唯一名称的列表。但是,如果你想要的是唯一名称的计数,那么我可能会在awk中做所有事情。事实上,我很想用awk而不是while循环。

如果要计算每个唯一名称的出现次数,则使用sort | uniq -c而不是sort -u。统计上的选项和变体是军团 - 关键点是你需要将逗号分割为/etc/group文件的最后一个字段。如果您出于某种原因在列表中有空格,则可能也必须删除这些空格。 tr ', ' '\n'会这样做。

使用awk,你可以这样做:

awk -F: '{ n = split($4, a, ","); for (u = 1; u <= n; u++) count[a[u]]++i } 
     END { for (u in count) print u, count[u] }' /etc/group 

它拆分第四场入阵a,然后计算每个名字的出现的count阵列英寸最后,它打印count阵列的条目。在我的Mac上,它取得了以下效果:

root 11 
_warmd 1 
_locationd 1 
_jabber 2 
_taskgated 1 
_postfix 2 
_devicemgr 4 
_calendar 3 
_cyrus 1 
_teamsserver 6 
_dovecot 1 
_xserverdocs 1 
_eppc 1 
_softwareupdate 1 

您可以根据需要进行进一步排序。

+0

我试图运行你的代码,但得到这个错误:'语法错误附近意外的令牌“;”如果[-n“$用户名”];' – s1ddok

+0

有一个(重大)错误 - 这是当你不检查时会发生什么。我已经用测试过的代码更新了shell代码(在数组赋值的内容中添加了'$(...)',您仍然需要调整命令以获得所需的结果(例如,使用'sort | uniq -c ''以获得每个名称的计数) –

+0

好吧!看起来正是我需要的东西 – s1ddok

2

你可以尝试这样的事情:

awk -F ':' '{ if(length($4)) { gsub(",", "\n", $4); print $4 } }' /etc/group | \ 
    sort | uniq -c 

awk命令将所有非空第四字段(':'作为分隔符),并与取代''“\ n '万一一组有多个用户。

然后,我们对独特的外观进行排序和计数。

编辑:

没有awk

cut -d: -f4 /etc/group | tr ',' '\n' | grep -v '^$' | sort | uniq -c 
+0

如果将第四个字段拆分为一个数组,然后使用该数组生成awk中每个用户名的计数,然后你会在最后打印。 –

+0

对不起,我不能使用AWK,我需要纯粹的bash解决方案 – s1ddok

+0

对不起,我不清楚问题的要求,upvoted for awk – s1ddok

0

稍加修改示例输入到包括,分隔的名字

$ cat abc.txt 
nobody:*:-2: 
nogroup:*:-1: 
wheel:*:0:root 
daemon:*:1:root 
kmem:*:2:root,test 
sys:*:3:root 
tty:*:4:root,t1,test 
operator:*:5:root 
mail:*:6:_teamsserver 

$ perl -F: -le 'foreach (split /,/,$F[3]){$h{$_}++ if /./} END{foreach (keys %h){print "$_ $h{$_}"}}' abc.txt 
t1 1 
_teamsserver 1 
root 6 
test 2 
  • -F:分割输入线上:并保存到@F阵列
  • foreach (split /,/,$F[3])迭代在第四场分上,
  • $h{$_}++ if /./增量散列如果非空
  • END{foreach (keys %h){print "$_ $h{$_}"}}打印所需格式的哈希信息
+0

对不起,我不能使用perl。我需要纯粹的bash解决方案 – s1ddok

+1

@ s1ddok:“纯粹的bash”是什么意思? Perl是一个命令; Awk是一个命令; 'tr'是一个命令; 'sort'是一个命令; 'uniq'是一个命令:没有一个是'纯Bash',因为Bash运行另一个命令。 –

+0

@JonathanLeffler我可能听起来像一个新手,因为我从来没有经历过bash编程,我想说的是我不能使用除默认命令以外的任何其他脚本语言 – s1ddok

1
cut -d: -f4 /etc/group | tr , '\n' | grep '.' | \ 
sort | uniq -c | join -a 1 -o '1.2,1.1' - /dev/null 

或者:

cut -d: -f4 /etc/group | tr , '\n' | grep '.' | \ 
sort | uniq -c | awk '{ print $2 " " $1 }' 

工作原理:

  1. cut了实地#4
  2. tr将逗号更改为换行符。
  3. grep删除空白行。
  4. sort,count uniq ue lines,print using OP's spec。
+1

为什么“rev”?你可以简单地使用'cut -d:-f4'而不需要'rev'。 –

+0

我不认为我理解这是如何工作,但upvoted – s1ddok

+0

@JonathanLeffler,你是对的,谢谢。 (对/ etc/group有一种模糊的理解,我没有想到域的数量,只是需要* last *)。 – agc