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 / 问题 / 474232
Accepted
agc
agc
Asked: 2018-10-10 04:08:44 +0800 CST2018-10-10 04:08:44 +0800 CST 2018-10-10 04:08:44 +0800 CST

为什么“粘贴”不能在标准错误旁边打印标准输入?

  • 772

通常在相邻列中paste打印两个命名(或等效)文件,如下所示:

paste <(printf '%s\n' a b) <(seq 2)

输出:

a   1
b   2

但是当这两个文件是/dev/stdinand/dev/stderr时,它的工作方式似乎不同。

假设我们有b缺少b框程序,它在标准输出上输出两行,在标准错误上输出两行。为了说明的目的,这可以用一个函数来模拟:

bb() { seq 2 | tee >(sed 's/^/e/' > /dev/stderr) ; }

现在运行annotate-output,(在Debian/Ubuntu/etc.上的devscripts包中),以显示它可以工作:

annotate-output bash -c 'bb() { seq 2 | tee >(sed 's/^/e/' > /dev/stderr) ; }; bb'
22:06:17 I: Started bash -c bb() { seq 2 | tee >(sed s/^/e/ > /dev/stderr) ; }; bb
22:06:17 O: 1
22:06:17 E: e1
22:06:17 O: 2
22:06:17 E: e2
22:06:17 I: Finished with exitcode 0

所以它有效。喂给:bb_paste

bb | paste /dev/stdin /dev/stderr

输出:

1   e1
e2
^C

它挂起 -^C意味着按Control-C退出。

更改|为 a;也不起作用:

bb ; paste /dev/stdin /dev/stderr

输出:

1
2
e1
e2
^C

也挂起 -^C表示按Control-C退出。

期望的输出:

1    e1
2    e2

可以使用paste吗?如果不是,为什么不呢?

shell io-redirection
  • 3 3 个回答
  • 1034 Views

3 个回答

  • Voted
  1. Best Answer
    JigglyNaga
    2018-10-10T09:30:59+08:002018-10-10T09:30:59+08:00

    为什么不能使用 /dev/stderr 作为管道

    问题不paste在于 ,也不在于/dev/stdin。它与/dev/stderr.

    所有命令都使用一个打开的输入描述符(0:标准输入)和两个输出(1:标准输出和 2:标准错误)创建。这些通常可以分别使用 names和访问/dev/stdin,但请参阅/dev/stdin、/dev/stdout 和 /dev/stderr 的可移植性如何?. 许多命令,包括,也会将文件名解释为 STDIN。/dev/stdout/dev/stderrpaste-

    当您单独运行bb时,STDOUT 和 STDERR 都是控制台,通常会出现命令输出。这些行通过不同的描述符(如您的 所示annotate-output)但最终在同一个地方结束。

    当您添加一个|和第二个命令时,创建一个管道......

    bb | paste /dev/stdin /dev/stderr
    

    |告诉 shell 将 的输出连接到bb的输入paste。 paste首先尝试从 读取/dev/stdin,它(通过一些符号链接)解析为它自己的标准输入描述符(shell 刚刚连接),因此该行1通过。

    但是外壳/管道对 STDERR 没有任何作用。 bb仍然将(e1 e2等)发送到控制台。同时,paste尝试从挂起的同一个控制台读取(直到您输入某些内容)。

    您的链接为什么我不能使用文本编辑器阅读 /dev/stdout?在这里仍然相关,因为同样的限制适用于/dev/stderr.

    如何制作第二个管道

    您有一个同时产生标准输出和标准错误的命令,并且您希望paste这两行彼此相邻。这意味着两个并发管道,每列一个。shell 管道... | ...提供了其中之一,您需要自己创建第二个,并将 STDERR 重定向到使用2>filename.

    mkfifo RHS
    bb 2>RHS | paste /dev/stdin RHS
    

    如果这是在脚本中使用,您可能更愿意在临时目录中创建该 FIFO,并在使用后将其删除。

    • 4
  2. mosvy
    2018-10-11T14:31:47+08:002018-10-11T14:31:47+08:00

    annotate-output能够做到这一点是因为它正在做一些特殊的事情(即将命令的标准错误重定向到先进先出),这是paste绝对无法做到的事情——仅仅是因为paste它没有运行它自己从它获取输入的命令,并且它无法重定向它们的输入或输出。

    但是您可以编写一个使用与注释输出完全相同的技巧的包装器:

    pasteout(){
      f=$(mktemp -u) || return
      mkfifo -m 600 -- "$f" || return
      "$@" 2>"$f" | paste -- - "$f"
      rm -f -- "$f"
    }
    pasteout bb
    

    但是请注意,它很容易出现死锁。例如,如果bb产生的标准输出超出了管道的容量,加上最初读取的额外量paste但不产生任何错误输出,paste则将被阻塞以等待 fifo 上的输入,并且不会清空bb正在将其标准输出提供给的管道,导致bb管道的 write()s 也挂起。

    • 3
  3. user232326
    2018-10-11T17:03:53+08:002018-10-11T17:03:53+08:00

    整条线路有几个问题需要我们分析,即:

    seq 2 | tee >(sed 's/^/e/' > /dev/stderr) | paste /dev/stdin /dev/stderr
    

    标准错误

    首先,最后一个命令。只有stdout可以通过管道:

    $ seq2 | paste -
    1
    2
    
    $ seq2 | paste - -
    1 2
    

    没有什么可读取的stderr:

    $ seq 2 | paste - /dev/stderr 
    1   ^C
    

    你需要^C它,因为它会阻塞,没有什么可以读取的stderr。
    即使您创建一些输出,stderr它也不会通过管道:

    $ { seq 2; seq 3 4 >/dev/stderr; } | paste - /dev/stderr
    1   3
    4
    

    和以前一样,1打印出来的paste块等待stderr.
    其他 2 个数字直接进入控制台并(独立)打印出来。

    您可以stderr在管道的最后一个命令中提供一些输入:

    $ { seq 2; seq 3 4 >/dev/stderr; } | paste - /dev/stderr 2</dev/null
    1
    2
    3
    4
    

    这与顺便说一句完全相同2>/dev/null,以避免阻塞命令中使用的第二个文件描述符paste。但是打印的值直接来自seq 3 4重定向到控制台,而不是来自paste. 这也是一样的:

    $ { seq 2; seq 3 4 >/dev/tty; } | paste - /dev/stderr 2</dev/null
    1   
    2   
    3
    4
    

    这不会阻止:

    $ seq 2 | tee >(sed 's/^/e/' > /dev/stderr) | 
      paste /dev/stdin /dev/stderr 2</dev/null
    1   
    2   
    e1
    e2
    

    命令

    其次, 的输出tee不必“按顺序”。`tee` 和 `bash` 进程替换顺序

    而且,事实上:流程替换的输出不必是“按顺序” 的:流程替换输出是无序的

    $ echo one; echo two > >(cat); echo three;
    one
    three
    two
    

    事实上,在某些示例中,如果您尝试多次,您可能会得到不同的订单。通过进程替换同时运行的独立进程的非确定性输出

    $ printf '%s\n' {0..1000} | tee >(head -n2) >(sort -grk1,1 | head -n3) >/dev/null
    1000
    999
    998
    0
    1
    

    所以,不,它不能通过过程替换和粘贴来完成。
    你需要给执行一些命令:

    $ seq 2 | { while read a; do printf "%s %s\n" "$a" "e$a" ; done; }
    1 e1
    2 e2
    

    bb

    因此,您的 bb 函数(基本上)包含:

    | tee >(sed 's/^/e/')
    

    可以通过以下方式进行测试:

    $ printf '%s\n' {0..1000} | tee >(sort -grk1,1 | head -n3 >&2) | head -n 2
    0
    1
    291
    290
    289
    

    应该按顺序打印 0、1、1000、999、998,但很多时候不是。
    那就是:它本质上是不稳定的。

    稳定的真实解决方案。

    bb 唯一安全的解决方案是避免任何进程替换。
    并且,利用{…}捕获 stdout 和 stderr 的优势,例如:

    $ bash -c '{ echo test-str >/dev/stderr; }' 2>/dev/null
    

    没有输出,去掉2确认。

    这将适用于 bb:

    $ bb() { seq 5 | tee /dev/stderr | sed 's/^/e/'; }
    

    并使用 fifo 进行粘贴:

    $ mkfifo out2
    $ bb 2>out2  | paste out2 -
    1   e1
    2   e2
    3   e3
    4   e4
    5   e5
    

    您需要设置一个陷阱来删除 fifo 文件,并在创建 fifo 文件之前测试它是否存在。

    似乎可以在我测试的所有 shell(与 Almquist 语法兼容)上便携工作。未完全测试,请其他用户确认,可能会有一些未知的惊喜。

    • 1

相关问题

  • 这个命令是如何工作的?mkfifo /tmp/f; 猫/tmp/f | /bin/sh -i 2>&1 | 数控 -l 1234 > /tmp/f

  • FreeBSD 的 sh:列出函数

  • 有没有办法让 ls 只显示某些目录的隐藏文件?

  • grep -v grep 有什么作用

  • 如何将带有〜的路径保存到变量中?

Sidebar

Stats

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

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

    • 4 个回答
  • Marko Smith

    ssh 无法协商:“找不到匹配的密码”,正在拒绝 cbc

    • 4 个回答
  • Marko Smith

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

    • 5 个回答
  • Marko Smith

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

    • 3 个回答
  • Marko Smith

    如何卸载内核模块“nvidia-drm”?

    • 13 个回答
  • 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
    rocky 如何将 GPG 私钥和公钥导出到文件 2018-11-16 05:36:15 +0800 CST
  • Martin Hope
    Wong Jia Hau ssh-add 返回:“连接代理时出错:没有这样的文件或目录” 2018-08-24 23:28:13 +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
  • Martin Hope
    Bagas Sanjaya 为什么 Linux 使用 LF 作为换行符? 2017-12-20 05:48:21 +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