【问题标题】:Uniq on array, not displaying correctly数组上的 Uniq,无法正确显示
【发布时间】:2021-03-18 18:18:31
【问题描述】:

我是 bash 编程的新手,我正在为数组以及如何使用它们而苦苦挣扎。 场景:

我有一个名为 x 的变量,它由一组 IP 组成。 这是我从脚本中 echo $x 时的输出

182.100.67.59 182.100.67.59 182.100.67.59 182.100.67.59 182.100.67.59 182.100.67.59 182.100.67.59 182.100.67.59 182.100.67.59 182.100.67.59 182.100.67.59 182.100.67.59 182.100.67.59 182.100.67.59 182.100.67.59 201.21.24.22 201.21.24.22 201.21.24.22
44.21.25.31 44.21.25.31 44.21.25.31 44.21.25.31
 

然后,我想知道每个 IP 重复了多少次。 期望的输出是:

15 182.100.67.59
4 44.21.25.31
3 201.21.24.22

我已经尝试了以下

(IFS=" "; sort <<< "$x" ) | uniq -c

输出 注意输出中的第一个 1。

**1** 182.100.67.59 182.100.67.59 182.100.67.59 182.100.67.59 182.100.67.59 182.100.67.59 182.100.67.59 182.100.67.59 182.100.67.59 182.100.67.59 182.100.67.59 182.100.67.59 182.100.67.59 182.100.67.59 182.100.67.59 201.21.24.22 201.21.24.22 201.21.24.22 44.21.25.31 44.21.25.31 44.21.25.31 44.21.25.31 

我没有看到这个,应该很简单,但我找不到解决方案:( 谢谢!还有非常棒的社区!

【问题讨论】:

  • IFS 改变了 shell 将记录拆分为字段的方式,但在 sort &lt;&lt;&lt; "$x" 中根本没有将记录拆分为字段;整个变量按原样在sort(这是一个单独的程序,不是外壳的一部分)的标准输入上传递,外壳没有以任何方式改变它。
  • 另请注意,echo $x 对您的变量 x 实际包含的内容非常缺乏信息。它将所有空格的运行更改为每个空格(隐藏换行符),将任何可以解析为 glob 表达式的内容替换为 glob 标识的文件列表,如果您的变量是数组,则静默丢弃除第一项之外的所有内容,并且还有一些其他最可能不需要的行为。最好使用declare -p x,尽管echo "$x" 总比没有改进要好。
  • 哦,谢谢@CharlesDuffy,所以问题出在 IFS 上。很高兴知道,您认为解决此问题的最佳方法是什么?
  • 我的意思更多的是 IFS 与它没有任何关系(因为没有发生字段拆分,控制字段拆分的变量在这里不相关)。评论的目的更多是为了解释为什么在设置IFS 后调用sort 的尝试不起作用。

标签: arrays linux bash loops


【解决方案1】:

方法一:
使用tr 将空格替换为换行符,后跟sort | uniq -c

tr ' ' '\n' <<<"$x" | sort | uniq -c

方法二:
使用echoxargs -n1 每行写入一个IP,然后是sort | uniq -c。注意xargs在这里比tr慢,而且可能有潜在的副作用,比如去掉引号:

echo "$x" | xargs -n1 | sort | uniq -c

【讨论】:

    【解决方案2】:

    这里不需要数组。

    使用grepsortuniq 的简单管道可以解决问题:

    echo "$x" | grep -Eo '[0-9]+(\.[0-9]+){3}' | sort | uniq -c | sort -rn
    

    【讨论】:

      【解决方案3】:

      实际上,您本身并没有数组;你有一串用空格分隔的数字。如果echo $x 给出了您指定的值,就是这种情况,答案是:

      aips=( ${x} )
      printf "%s\n" "${aips[@]}"  | sort | uniq -c
      

      这会将由空格分隔的数字字符串 (IFS) 转换为数组 aips(括号指定这是一个数组而不是字符串),然后使用 printf 的行为,它重新- 当参数多于格式规范指定时使用格式规范,打印数组的每个元素 (${aips[@]}),后跟换行符,然后排序并uniqs 它。

      如果这是一个真正的数组(例如x=( "182.100.67.59" "201.21.24.22" ...)),答案将只是后半部分:

      printf "%s\n" "${x[@]}" | sort | uniq -c
      
       15 182.100.67.59
        3 201.21.24.22
        4 44.21.25.31
      

      【讨论】:

      • 鉴于 echo $x 显示了所有 IP,它显然不是一个数组(或者 OP 使用的是 zsh 而不是 bash——但在 bash 中,$array 仅扩展为第一个元素) .
      • @CharlesDuffy Point 很好,我回答了标题中的问题,而不是问题中详细说明的问题。我会更新我的答案。
      • 请注意 aips=( ${x} )considered an antipattern -- 它不只是在 IFS 上将项目拆分为一个数组,而且还会扩展内容中的 glob,并且相应地取决于 shell 的各种 glob 相关配置(nullglobfailglob 等)。 read -r -a aips &lt;&lt;&lt;"$x" 更容易预测。
      • (完整性警告:如果一个变量可能有多行,则可能需要read -r -d '' -a apis &lt; &lt;(printf '%s\0' "$aips");没有-dread 将只消耗一行;with -d,它会一直读取,直到看到分隔符或 EOF,但如果没有看到分隔符,将返回 false ——因此在字符串成功写入后在末尾注入 NUL。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-03-02
      • 2019-09-15
      • 2019-09-28
      • 2018-07-21
      相关资源
      最近更新 更多