我怎么可以送stdout的一个过程的多个程序中使用(优选的无名)管在Unix(或Windows)?

StackOverflow https://stackoverflow.com/questions/60942

我想向stdout的过程proc1到两个进程proc2和proc3:

         proc2 -> stdout
       /
 proc1
       \ 
         proc3 -> stdout

我试过了

 proc1 | (proc2 & proc3)

但它似乎没有工作,即

 echo 123 | (tr 1 a & tr 1 b)

 b23

到stdout而不是的

 a23
 b23
有帮助吗?

解决方案

编者注:
- >(…) 是一个 进程替换 这是一个 非标准壳的特征一些 POSIX兼容弹: bash, ksh, zsh.
-这回答意外发送出处理替代的输出通过管道 : echo 123 | tee >(tr 1 a) | tr 1 b.
-输出的过程替代将是不可预知的交织,并且,除非在 zsh, 管道可以终止之前的命令 >(…) 做。

在unix(或mac),使用 tee 命令:

$ echo 123 | tee >(tr 1 a) >(tr 1 b) >/dev/null
b23
a23

通常会使用 tee 重新输出到多个文件,但使用>(...)可以 重定向到另一个进程。因此,在一般情况下,

$ proc1 | tee >(proc2) ... >(procN-1) >(procN) >/dev/null

会做你想要什么。

窗下,我不认为建立在壳具有相等。微软的 Windows PowerShell 有一个 tee 命令虽然。

其他提示

像dF说, bash 允许使用 >(…) 构造运行命令的文件。(还有 <(…) 建造替代的 输出 另一个命令,在地方一名,但是这是无关紧要的现在,我提到它只是为了完整性).

如果你没有庆典,或运行上的一个系统与旧版本的庆典,你可以手工做的什么庆典,通过使用先进先出的文件。

通用的方式来实现你想要什么,是:

  • 决定许多过程应该得到的输出你的命令,并创造尽可能多的Fifo,最好是在一个全球性的临时文件夹:
    subprocesses="a b c d"
    mypid=$$
    for i in $subprocesses # this way we are compatible with all sh-derived shells  
    do
        mkfifo /tmp/pipe.$mypid.$i
    done
  • 开始你所有的子进程在等待输入从Fifo:
    for i in $subprocesses
    do
        tr 1 $i </tmp/pipe.$mypid.$i & # background!
    done
  • 执行你的命令准备的Fifo:
    proc1 | tee $(for i in $subprocesses; do echo /tmp/pipe.$mypid.$i; done)
  • 最后,删除Fifo:
    for i in $subprocesses; do rm /tmp/pipe.$mypid.$i; done

注:由于兼容性原因,我会做的 $(…) 与反引号,但我不能这样做,编写本答复(该backquote是使用如此)。通常, $(…) 是老足够的工作,即使在旧版本的股,但如果没有,封闭 部分反引号.

Unix(bash, ksh, zsh)

dF。's答案 包含 种子 一个答案的基础上 tee输出 工艺的替换
(>(...)), 可以或不可以 工作,根据你的要求:

注意,处理替换一个 非标准 功能(主要) POSIX-拥有-只炮弹如 dash (其作为 /bin/sh 在Ubuntu, 例如),做 支持。脚本目标 /bin/sh 应该 依靠他们。

echo 123 | tee >(tr 1 a) >(tr 1 b) >/dev/null

陷阱 这种方法是:

  • 不可预知、异步的输出行为:输出流从命令的内部产出的过程替换 >(...) 交织在不可预测的方式。

  • bashksh (而不是 zsh -但是看到的异常低):

    • 输出的可能到达 该命令已经完成。
    • 随后命令可以开始执行 之前 该命令过程中替换已经完成了 - bashksh 等待输过程中的替代产生的进程完成,至少默认。
    • 执行局联席会议 把它放在一个评论dF。's的回答:

要知道,命令开始的内部 >(...) 是脱离原壳,你不能轻易确定他们何时完成;的 tee 将完成后编写的一切,但所取代的进程将仍然是耗费数据从各种缓冲器,在核心文件I/O,再加上任何时间通过他们内部处理的数据。你可能会遇到比赛的条件下如果你的外壳然后继续依靠任何东西生产的子过程。

  • zsh 是只外壳, 通过默认等待的过程在运行过程的输出的替换的完成, 除了 如果它是 stderr 这是重新定向的一个(2> >(...)).

  • ksh (至少作为的版本 93u+)允许使用的参数-不 wait 等待输过程中的替代产生的进程完成。
    请注意,在一个互动的话,可能导致在等待任何未决的 背景的工作 太多,但。

  • bash v4.4+ 可以等待 最近 推出的产出过程中取代与 wait $!, 但论点-不 wait 的工作,使得这种不合适的命令 输过程中替换。

  • 但是, bashksh强迫 要等待 通过管道命令 | cat, 但注意,这使得该命令在运行 子shell. 需要注意的事项:

    • ksh (作为的 ksh 93u+)不支持在发送 stderr 一个输出过程取代(2> >(...));这种尝试是 忽略.

    • 同时 zsh 是(值得赞扬)同步 通过默认 与(更为常见) stdout 输出过程的替代,即使是 | cat 技术不能使他们同步 stderr 输过程中替换(2> >(...)).

  • 但是, 即使你保证 同步执行, 的问题 不可预知的交织的产出 仍然存在。

将以下命令,当在运行 bashksh, 说明问题的行为(你可能已运行好几次看到 症状):的 AFTER 通常将打印 之前 从输出替换和输出从后者可以是交织的不可预测。

printf 'line %s\n' {1..30} | tee >(cat -n) >(cat -n) >/dev/null; echo AFTER

在短短的:

  • 保证一个特定的每指令输出序列:

    • 既没有 bash 也不 ksh 也不 zsh 支持。
  • 同步执行:

    • 可行的,除非有 stderr来源输出过程取代:
      • zsh, 他们 总是 异步的。
      • ksh, 他们 不在所有的工作.

如果你能活着的这些局限性,使用的输出进程的替代是一个可行的选择(例如,如果所有的人都写信给输出单独的文件)。


注意, tzot更多的繁琐,但能POSIX符合解决方案 也呈现不可预知的输出行为;然而,通过使用 wait 你可以确保随后的命令没有开始执行,直到所有背景的过程已经完成。
看看底部 对于 一个更强大的同步化产出执行情况.


唯一的 简单的 bash 解决方案 与可预测输出行为 是的以下,其中,但是,是 过于缓慢的大型输入的设置, 因为壳循环本质上都是缓慢的。
还注意到这个 候补委员 输出行从目标的命令.

while IFS= read -r line; do 
  tr 1 a <<<"$line"
  tr 1 b <<<"$line"
done < <(echo '123')

Unix(使用GNU平行)

安装 GNU parallel 能够使一个 可靠的解决方案化(每命令)输出 此外,允许 平行的执行:

$ echo '123' | parallel --pipe --tee {} ::: 'tr 1 a' 'tr 1 b'
a23
b23

parallel 默认情况下,确保输出从不同的命令不交织(这种行为可以修改--见 man parallel).

注:一些Linux发行版来 不同的 parallel 工具,它不会与上述命令;使用 parallel --version 确定哪一项,如果有,你有。


Windows

杰Bazuzi是有用的答案 显示如何做到这一点 PowerShell.这说:他的回答是模拟的循环 bash 答复所述,它将 过于缓慢的大型输入的设置 并且还 候补委员 输出行从目标的命令.



bash-根据,否则便携式Unix解决方案同步执行和输出化

以下是一个简单的,但合理稳健的执行情况的方式呈现 tzot的答案 此外,提供:

  • 同步执行
  • 化(分组)输出

虽然没有严格POSIX兼容的,因为它是一个 bash 脚本,它应该是 便携式到任何Unix平台,已 bash.

注:你可以找到一个更全面的执行情况发布了根据麻省理工学院许可证 这一要旨.

如果保存的代码如下的脚本 fanout, ,使其可执行,并把int你 PATH, ,命令从该问题的工作如下:

$ echo 123 | fanout 'tr 1 a' 'tr 1 b'
# tr 1 a
a23
# tr 1 b
b23

fanout 脚本的源代码:

#!/usr/bin/env bash

# The commands to pipe to, passed as a single string each.
aCmds=( "$@" )

# Create a temp. directory to hold all FIFOs and captured output.
tmpDir="${TMPDIR:-/tmp}/$kTHIS_NAME-$$-$(date +%s)-$RANDOM"
mkdir "$tmpDir" || exit
# Set up a trap that automatically removes the temp dir. when this script
# exits.
trap 'rm -rf "$tmpDir"' EXIT 

# Determine the number padding for the sequential FIFO / output-capture names, 
# so that *alphabetic* sorting, as done by *globbing* is equivalent to
# *numerical* sorting.
maxNdx=$(( $# - 1 ))
fmtString="%0${#maxNdx}d"

# Create the FIFO and output-capture filename arrays
aFifos=() aOutFiles=()
for (( i = 0; i <= maxNdx; ++i )); do
  printf -v suffix "$fmtString" $i
  aFifos[i]="$tmpDir/fifo-$suffix"
  aOutFiles[i]="$tmpDir/out-$suffix"
done

# Create the FIFOs.
mkfifo "${aFifos[@]}" || exit

# Start all commands in the background, each reading from a dedicated FIFO.
for (( i = 0; i <= maxNdx; ++i )); do
  fifo=${aFifos[i]}
  outFile=${aOutFiles[i]}
  cmd=${aCmds[i]}
  printf '# %s\n' "$cmd" > "$outFile"
  eval "$cmd" < "$fifo" >> "$outFile" &
done

# Now tee stdin to all FIFOs.
tee "${aFifos[@]}" >/dev/null || exit

# Wait for all background processes to finish.
wait

# Print all captured stdout output, grouped by target command, in sequences.
cat "${aOutFiles[@]}"

由于@dF:提到,PowerShell有开球,我认为我显示的一种方式做到这一点置。

PS > "123" | % { 
    $_.Replace( "1", "a"), 
    $_.Replace( "2", "b" ) 
}

a23
1b3

注意到各个对象出来的第一命令之前先处理的下一个目的是创建。这可以允许扩展到非常大的投入。

另一种方式来做的就是,

 eval `echo '&& echo 123 |'{'tr 1 a','tr 1 b'} | sed -n 's/^&&//gp'`

输出:

a23
b23

没有必要创建一个子shell在这里

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top