AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • 主页
  • 系统&网络
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • 主页
  • 系统&网络
    • 最新
    • 热门
    • 标签
  • Ubuntu
    • 最新
    • 热门
    • 标签
  • Unix
    • 最新
    • 标签
  • DBA
    • 最新
    • 标签
  • Computer
    • 最新
    • 标签
  • Coding
    • 最新
    • 标签
主页 / unix / 问题 / 553866
Accepted
Lucas
Lucas
Asked: 2019-11-25 03:10:37 +0800 CST2019-11-25 03:10:37 +0800 CST 2019-11-25 03:10:37 +0800 CST

如何计算管道中间的线数

  • 772

我想计算管道中的行数,然后根据结果继续管道。

我试过了

x=$(printf 'faa\nbor\nbaz\n' \
  | tee /dev/stderr | wc -l) 2>&1 \
  | if [[ $x -ge 2 ]]; then
      grep a
    else
      grep b
    fi

但它根本不过滤(“a”和“b”都没有)。这是非常出乎意料的,因为至少这些工作符合预期:

printf 'faa\nbor\nbaz\n' | if true; then grep a; else grep b; fi
printf 'faa\nbor\nbaz\n' | if false; then grep a; else grep b; fi

似乎我无法从命令替换内部重定向标准错误,因为这也不起作用(在 bash 中)。它打印所有三行:

x=$(printf 'faa\nbor\nbaz\n' | tee /dev/stderr | wc -l) 2>&1 | grep a

在 zsh 中它只打印两行。

但是在两个 shell 中,变量 x 在管道之后都没有设置,甚至在管道的后半部分也没有设置。

我该怎么做才能计算管道中的行数,然后根据该数字采取行动?我想避免使用临时文件。

bash zsh
  • 2 2 个回答
  • 1096 Views

2 个回答

  • Voted
  1. Best Answer
    Kamil Maciorowski
    2019-11-25T15:29:28+08:002019-11-25T15:29:28+08:00

    这个评论是真的:

    管道的每个部分都独立于同一管道的其他部分启动。这意味着$x如果它设置在其他阶段之一,则无法在管道中间使用。

    这并不意味着你不能做任何事情。管道可能被认为是主要的数据通道,但进程仍然可以使用侧通道进行通信:文件、命名 fifo 或其他任何东西(尽管有时您需要格外小心,不要让它们阻塞)。

    您想计算行数并稍后有条件地处理整个数据流。这意味着您需要到达流的末尾,然后才能传递整个流。所以你需要以某种方式保存整个流。临时文件看起来像是一种理智的方法。您应该将管道分成至少两部分。第一部分应将数据保存在文件中;然后应该计算行数(我认为这个任务可能属于第一部分);然后最后一部分应该得到数字,从头开始读取文件以接收数据,并采取相应的行动。


    如果您真的想避免使用临时文件,那么您的管道的某些部分应该以某种方式表现得像sponge. 为避免旁通道,应将行数作为输出的第一行传递,并且管道的其余部分应理解此协议。

    考虑这个命令:

    sed '$ {=; H; g; p;}; H; d'
    

    它在保持空间中累积线。如果至少有一行,则在收到最后一行后sed打印行数,然后是空行和实际输入。

    空行是不必要的,但从这个简单的代码中“自然”地出现。我不会试图在 中避免它sed,而是稍后在管道中处理它(例如,使用sed '2 d')。

    示例用法:

    #!/bin/sh
    
    sed '$ {=; H; g; p;}; H; d' | sed '2 d' | {
       if ! IFS= read -r nlines; then
          echo "0 lines. Nothing to do." >&2
       else
          echo "$nlines lines. Processing accordingly." >&2
          if [ "$nlines" -ge 2 ]; then
             grep a
          else
             grep b
          fi
       fi
    }
    

    笔记:

    • IFS= read -r是一个矫枉过正,因为第一行定义明确,它包含一个唯一的数字(或它不存在)。
    • 我用过/bin/sh。该代码也将在 Bash 中运行。
    • 您不能假设sed能够保存任意数量的数据。POSIX 规范说:

      模式和保持空间都应该能够保持至少 8192 字节。

      所以它的限制可能只有 8192 字节。另一方面,我可以想象一个临时文件很容易保存 1TB 的数据。也许不要不惜一切代价避免临时文件。


    标题说“计算行数”,但您的示例试图确定数字是否为 2 或更多(通常为 N 或更多)。这些问题是不等价的。在输入第二(N)行之后,您知道后一个问题的答案,甚至行将无限期地出现。上面的代码不能处理不确定的输入。让我们在某种程度上修复它。

    sed '
    7~1 {p; d}
    6 {H; g; i \
    6+
    p; d}
    $ {=; H; g; p}
    6! {H; d}
    '
    

    此命令的行为与之前的解决方案类似,但当它到达第 6 行时,它假定(打印)行数为6+. 然后打印已经看到的行,并在它们出现后立即打印以下行(如果有的话)(类似cat行为)。

    示例用法:

    #!/bin/sh
    
    threshold=6
    
    sed "
    $((threshold+1))~1 {p; d}
    $threshold {H; g; i \
    $threshold+
    p; d}
    $ {=; H; g; p}
    ${threshold}! {H; d}
    " | sed '2 d' | {
       if ! IFS= read -r nlines; then
          echo "0 lines. Nothing to do." >&2
       else
          echo "$nlines lines. Processing accordingly." >&2
          if [ "$nlines" = "$threshold+" ]; then
             grep a
          else
             grep b
          fi
       fi
    }
    

    笔记:

    • 修复了“在某种程度上”,因为sed(无论您的情况是什么限制)的限制仍然适用。但是现在sed最多处理几$threshold行;如果$threshold足够低,那么应该没问题。
    • 示例代码仅针对测试,$threshold+但协议允许您区分 0、1、2、...、阈值减一和阈值或更多行。

    我不是很熟练sed。如果我的sed代码可以简化,请在评论中给我提示。

    • 2
  2. Lucas
    2019-11-26T23:51:24+08:002019-11-26T23:51:24+08:00

    根据 Kamil 的讨论和 sed 代码,我找到了这个 awk 解决方案:

    awk -v th="$threshold" '
      function print_lines() { for (i in lines) print lines[i] }
      NR < th { lines[NR] = $0 }
      NR > th { print }
      NR == th { print th; print_lines(); print }
      END { if (NR < th) { print NR; print_lines(); } }' \
    | if read nlines; then
        if [ "$nlines" -eq "$threshold" ]; then
          grep a
        else
          grep b
        fi
      fi
    
    • 0

相关问题

  • 通过命令的标准输出以编程方式导出环境变量[重复]

  • 从文本文件传递变量的奇怪问题

  • 虽然行读取保持转义空间?

  • `tee` 和 `bash` 进程替换顺序

  • 运行一个非常慢的脚本直到它成功

Sidebar

Stats

  • 问题 205573
  • 回答 270741
  • 最佳答案 135370
  • 用户 68524
  • 热门
  • 回答
  • Marko Smith

    模块 i915 可能缺少固件 /lib/firmware/i915/*

    • 3 个回答
  • Marko Smith

    无法获取 jessie backports 存储库

    • 4 个回答
  • Marko Smith

    如何将 GPG 私钥和公钥导出到文件

    • 4 个回答
  • Marko Smith

    我们如何运行存储在变量中的命令?

    • 5 个回答
  • Marko Smith

    如何配置 systemd-resolved 和 systemd-networkd 以使用本地 DNS 服务器来解析本地域和远程 DNS 服务器来解析远程域?

    • 3 个回答
  • Marko Smith

    dist-upgrade 后 Kali Linux 中的 apt-get update 错误 [重复]

    • 2 个回答
  • Marko Smith

    如何从 systemctl 服务日志中查看最新的 x 行

    • 5 个回答
  • Marko Smith

    Nano - 跳转到文件末尾

    • 8 个回答
  • Marko Smith

    grub 错误:你需要先加载内核

    • 4 个回答
  • Marko Smith

    如何下载软件包而不是使用 apt-get 命令安装它?

    • 7 个回答
  • Martin Hope
    user12345 无法获取 jessie backports 存储库 2019-03-27 04:39:28 +0800 CST
  • Martin Hope
    Carl 为什么大多数 systemd 示例都包含 WantedBy=multi-user.target? 2019-03-15 11:49:25 +0800 CST
  • Martin Hope
    rocky 如何将 GPG 私钥和公钥导出到文件 2018-11-16 05:36:15 +0800 CST
  • Martin Hope
    Evan Carroll systemctl 状态显示:“状态:降级” 2018-06-03 18:48:17 +0800 CST
  • Martin Hope
    Tim 我们如何运行存储在变量中的命令? 2018-05-21 04:46:29 +0800 CST
  • Martin Hope
    Ankur S 为什么 /dev/null 是一个文件?为什么它的功能不作为一个简单的程序来实现? 2018-04-17 07:28:04 +0800 CST
  • Martin Hope
    user3191334 如何从 systemctl 服务日志中查看最新的 x 行 2018-02-07 00:14:16 +0800 CST
  • Martin Hope
    Marko Pacak Nano - 跳转到文件末尾 2018-02-01 01:53:03 +0800 CST
  • Martin Hope
    Kidburla 为什么真假这么大? 2018-01-26 12:14:47 +0800 CST
  • Martin Hope
    Christos Baziotis 在一个巨大的(70GB)、一行、文本文件中替换字符串 2017-12-30 06:58:33 +0800 CST

热门标签

linux bash debian shell-script text-processing ubuntu centos shell awk ssh

Explore

  • 主页
  • 问题
    • 最新
    • 热门
  • 标签
  • 帮助

Footer

AskOverflow.Dev

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve