linux - 在 Bash/Awk/Perl 中有效地按列计算 token

标签 linux bash perl awk

我有来自管道的以下输出。

Lorem dolor sit amet consectetur
Lorem ipsum dolor sit ,
Lorem dolor sit amet ,
Lorem dolor ipsum sit !

编辑

列数由我/用户指定,但不太可能超过 10。我目前最大的数据集如下所示:

prev_command | wc -l
$ 23805483

如果我没记错的话,有超过 300,000 个独特的 token 。


字段由空格分隔。字段可能包含各种特殊符号(→æ€...ð),但不能包含空格或制表符。我不一定知道列数。

对于每一列(字段),我需要对字数进行排序:本质上是 sort |唯一的-c |排序-nr。期望的输出:

4 Lorem 3 dolor 2 sit   2 amet 2 ,
        1 ipsum 1 dolor 2 sit  1 consectetur
                1 ipsum        1 !

理想情况下,值和键由空格分隔,每个 val-key 对由制表符分隔。由于长度不同,第 3 行实际上是 \s\t\s\t1\sipsum\t\s\t1\s!

到目前为止,我所拥有的是一个可以工作的 shell 脚本,但有几件事我不喜欢它。以下是相关行。

#!/usr/bin/env sh

tmp="$(mktemp -d)"
trap 'rm -rf -- "$tmp"' EXIT

data="${1:-/dev/stdin}"
[ "$data" != "$1" ] && cat $data > $tmp/table && data="$tmp/table"

count() {
    sort | uniq -c | sort -rn \
      | awk -v x=" " '{ print $1 x $2 }'
}

# infer column number from first line
ncol=$(head -1 $data | wc -w)

# loop over columns in parallel and save each in temporary file
for i in $(seq $ncol); do
  cut -f $i -d " " $data | count > $tmp/col$i &
done
wait

paste $tmp/col[0-9]*
  1. 如何直接在标准输入上获取数据流,并且仍然可以选择获取文件参数而不会损失太多速度?如果我删除带有临时文件的步骤,并将原始管道通过管道传输到脚本中,我仍然会得到输出,但不是预期的输出。不知道发生了什么。我尝试了 while read -ra...,但不知道在循环中要做什么。
  2. 有什么办法可以避免整个临时文件业务?在某些情况下,输入可能太大而无法保存在内存中。使用 paste 将输出转换为正确的格式也很痛苦,因为它是参差不齐的。当我只想要标签时,我目前将它通过管道传输到 sed -e 's/\t\t/\t\t\t/g' -e 's/^\t/\t\t/g '。布莱尔。与具有前导空格的 uniq -c 相同。
  3. 如何加快速度?我确实有一个 perl 版本的 count(),但它本质上是在做同样的事情,而不是处理 i/o。

我更喜欢 bash/awk 解决方案而不是 perl,因为我不太了解 perl,并且更喜欢我理解的解决方案(并且还可以最大限度地减少依赖关系)。但如果有人知道一种令人难以置信的快速 perl 方法,我很乐意接受。 :D

这个任务对我来说似乎很简单,但我找不到任何可以处理可变列和管道输入的东西。 提前感谢您的任何提示! :)

最佳答案

另一个awk解决方案

$ awk 'NR==FNR {for(i=1;i<=NF;i++) c[i,$i]++;next} 
               {f=line=""; 
                for(i=1;i<=NF;i++) 
                  {k=i SUBSEP $i; 
                   if(k in c) 
                     {f=1; line=line sprintf("%d %s",c[k],$i); delete c[k]}; 
                   line=line "\t"} 
                   if(f) print line}' file{,}


4 Lorem 3 dolor 2 sit   2 amet  1 consectetur
        1 ipsum 1 dolor 2 sit   2 ,
                1 ipsum         1 !

由 Ed Morton 编辑以显示由 gawk -o- pretty-print 的相同脚本:

NR == FNR {
    for (i = 1; i <= NF; i++) {
        c[i, $i]++
    }
    next
}

{
    f = line = ""
    for (i = 1; i <= NF; i++) {
        k = i SUBSEP $i
        if (k in c) {
            f = 1
            line = line sprintf("%d %s", c[k], $i)
            delete c[k]
        }
        line = line "\t"
    }
    if (f) {
        print line
    }
}

关于linux - 在 Bash/Awk/Perl 中有效地按列计算 token ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65886868/

相关文章:

linux - 如何处理 ".xsl"文件中的字符串数组?

linux - 没有在虚拟机中获得真正的文件更新

bash - 使用 Bash 脚本自动将 360° 3D 元数据注入(inject)视频

bash - 如何在搜索模式中使用带有 sed 的 xargs

multithreading - Perl:为要处理的线程正确传递数组

c - 将二进制文件移动到目录之外时出现段错误

git - 如何从提交哈希中分辨出 git 分支名称?

performance - Perl:为什么 if 语句比 "and"慢?

php - 如何使用 perl/php/grep/etc 从 csv 中提取日期范围?

c++ - 子进程能够更改父进程的 epoll 状态