【问题标题】:Sort and count unique entries in an array of strings对字符串数组中的唯一条目进行排序和计数
【发布时间】:2017-01-23 12:45:52
【问题描述】:

我需要做的是从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

【问题讨论】:

  • 请添加可验证的样本 i/p 和预期的 o/p。您的 /etc/group 文件和您的输出格式。
  • 试试cut -d: -f1 /etc/group | sort | uniq -c
  • 刚刚添加了想要的例子
  • @spasic:需要cut -d: -f4 /etc/group | tr ',' '\n' | sort -u | wc -l 来计算唯一名称,或者cut -d: -f4 /etc/group | tr ',' '\n' | sort | uniq -c 来计算每个唯一名称的出现次数。
  • 您需要在[:[ "$Username" != "" ]内的运算符周围添加空格。

标签: linux bash macos sorting unix


【解决方案1】:

每个“用户名”字段实际上是一个可选的以逗号分隔的空用户名列表。要分隔用户名,您需要用逗号分隔条目。

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

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

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

【讨论】:

  • 我正在尝试运行您的代码但收到此错误:syntax error near unexpected token ";" if [ -n "$Usernames" ];
  • 有一个(主要)错误——当你不检查时会发生这种情况。我已经用测试代码更新了 shell 代码(在数组赋值的内容周围添加了$( … )。你仍然需要调整命令以获得你想要的结果(例如,使用sort | uniq -c 来获取每个名称的计数) )。
  • 好的!看起来正是我所需要的。
  • 如果我们不谈论 awk、perl 等。您认为 IFS 方法好吗?例如,也许有更好的方法可以使用正则表达式...
  • 还有一个问题:有没有办法将唯一计数的结果输出为列,而不是在普通行中?
【解决方案2】:

你可以试试这样的:

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

awk 命令采用所有非空第 4 字段(以 ':' 作为分隔符)并将 ',' 替换为 '\n' 如果一个组有多个用户。

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

编辑:

没有awk

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

【讨论】:

  • 如果将第四个字段拆分为一个数组,然后使用该数组生成 awk 中每个用户名的计数,然后在最后打印。
  • 对不起,我不能使用 AWK,我需要纯 bash 解决方案
  • 对不起,我对问题的要求不清楚,赞成 awk
【解决方案3】:

稍微修改示例输入以包含 , 分隔名称

$ 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]), 上迭代第 4 个字段拆分
  • $h{$_}++ if /./ 如果非空则增加哈希
  • END{foreach (keys %h){print "$_ $h{$_}"}} 打印所需格式的哈希信息

【讨论】:

  • 对不起,我不能使用 perl。我需要纯 bash 解决方案
  • @s1ddok:“纯 Bash”是什么意思? Perl 是一个命令; awk 是一个命令; tr 是一个命令; sort 是一个命令; uniq 是一个命令:这些都不是“纯 Bash”,因为 Bash 运行另一个命令。
  • @JonathanLeffler 我可能听起来像个新手,因为我从未体验过 bash 编程,我想说的是我不能使用除默认命令之外的任何其他脚本语言
  • @s1ddok:你需要在问题中编辑这些限制——人们会使用标准命令(如 Awk、Perl、Python),除非你说它们不能使用。
【解决方案4】:
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,计数 unique 行,使用 OP 的规范打印。

【讨论】:

  • 为什么是rev?你可以简单地使用cut -d: -f4,而不需要rev
  • 我想我不明白它是如何工作的,但赞成
  • @JonathanLeffler,你是对的,谢谢。 (归咎于对/etc/group 的模糊理解。我没有假设字段的数量,只是需要最后一个。)适当修改。跨度>
猜你喜欢
  • 1970-01-01
  • 2015-07-27
  • 1970-01-01
  • 1970-01-01
  • 2016-07-18
  • 1970-01-01
相关资源
最近更新 更多