将stdout两次传递给同一命令

时间:2019-02-10 11:15:12

标签: linux bash

你好,我对如何将stdout两次传递给同一命令感兴趣...

例如,如果我运行以下命令:

        int size = int.Parse(System.Console.ReadLine());
        string[] words = new string[size];
        string combinedWord = "";
        for(int i = 0; i < size; i++)
        {
            words[i] = System.Console.ReadLine();
        }
        // Combine the strings in words[i] to combinedWord here
        // which I don't know how to do it.

然后我得到:

seq 5 >a
tac a >b
paste a b

以下内容也可以提供相同的结果:

1       5
2       4
3       3
4       2
5       1

paste <(seq 5) <(seq 5 |tac)

我只想使用seq 5 | paste - <(seq 5 |tac) 一次-可能类似于以下尝试,但不起作用:

seq 5

seq 5 | paste - <(tac -)

我希望某种文件描述符操作或进一步的进程替换魔术能够解决问题,但我很难到达那里。

4 个答案:

答案 0 :(得分:1)

除非使用tee之类的工具,否则无法复制单个流。常见的安排是使用临时文件。

#!/bin/bash
t=$(mktemp -t pastepaste.XXXXXXXXXX) || exit
trap 'rm -f "$t"' ERR EXIT
tee "$t" |
paste - <(tac "$t")

答案 1 :(得分:1)

您可以执行以下操作:

rootdir=$(mktemp -d)  # Strictly speaking not needed, but we create us a tempdir
mkfifo "$rootdir/pipe"  # create a named pipe
seq 5 | tee "$rootdir/pipe" | paste - <(tac "$rootdir/pipe")
# We could now tee (split) output into that pipe
rm -Rf "$rootdir"  # cleanup

您还可以包含额外的文件描述符,但是仍然需要一些东西来支撑它,以连接流的两端。问题是,尽管您可以多路输出,但是在一个新创建的问题上,您仍然只有一个输入流(将管道命令连接到先前过程的stdout时)。必须添加第二条传输路径。

tmpfile=$(mktemp); exec 3<>"$tmpfile"; rm "$tmpfile"

创建一个临时文件,然后打开fd 3进行读取/写入。一旦有了文件描述符,就不再需要文件系统中该文件的名称。

seq 5 | tee /proc/self/fd/3 | paste - <(tac </proc/self/fd/3)

我们仍然使用teestdout和fd 3之间分割输出。文件描述符是由子进程继承的,因此我们可以用/proc/self/来引用它们(从teetac)。

答案 2 :(得分:0)

您面临的问题是,管道中的每个元素都是在单独的子shell 中执行的,因此,由元素新重定向的FD不会反映在父shell中,这是做所有的I / O协调。

其他一些大师可能想出了一个神奇的重定向咒语来解决父级的这个问题,但是为了清楚起见,我建议使用命名管道(也称为FIFO)。由于它们存在于Shell环境之外,因此它们始终对所有管道元素可见。

更重要的是,使用正确的命名,“连接管道”将成为一个相对没有错误的过程,尤其是当您的管道“网络”变得更加复杂时。没有猜测哪个FD携带哪个输出的游戏。

以您的示例为例,它可以无缝运行:

#!/bin/bash
# Make a temp dir...
tmpd=$(mktemp -d)
# ...and clean it up automatically
trap 'rm -fr $tmpd' EXIT

# Create the FIFO...
mkfifo $tmpd/tac.fifo
# ...and PROFIT!
seq 5 | tee >(tac >$tmpd/tac.fifo) | paste - $tmpd/tac.fifo

进一步阅读:

答案 3 :(得分:0)

一个简单,对称的示例是使用两个命名管道。

 trap 'rm p1 p2' ERR EXIT
 mkfifo p1 p2

然后您可以让tactee的输出过滤到p2

 seq 5 | tee p1 <(tac > p2) > /dev/null &
 paste p1 p2

或在传递到p2之前过滤paste的内容。

seq 5 | tee p1 p2 > /dev/null
paste p1 <(tac p2)