拆分不同命令的输入并合并结果

拆分不同命令的输入并合并结果

我知道如何组合不同命令的结果

paste -t',' <(commanda) <(commandb)

我知道将相同的输入传递给不同的命令

cat myfile | tee >(commanda) >(commandb)

现在如何组合这些命令?这样我就能做到

cat myfile | tee >(commanda) >(commandb) | paste -t',' resulta resultb

说我有一个文件

我的文件:

1
2
3
4

我想制作一个新文件

1 4 2
2 3 4
3 2 6
4 1 8

我用了

cat myfile | tee >(tac) >(awk '{print $1*2}') | paste

会给我垂直的结果,我真的想按水平顺序粘贴它们。

答案1

当您进行多个进程替换时,不能保证以任何特定顺序获得输出,因此您最好坚持使用

paste -t',' <(commanda < file) <(commandb < file)

假设cat myfile代表一些昂贵的管道,我认为您必须将输出存储在文件或变量中:

output=$( some expensive pipeline )
paste -t',' <(commanda <<< "$output") <(commandb <<< "$output")

使用你的例子:

output=$( seq 4 )
paste -d' ' <(cat <<<"$output") <(tac <<<"$output") <(awk '$1*=2' <<<"$output")
1 4 2
2 3 4
3 2 6
4 1 8

另一个想法:FIFO 和单一管道

mkfifo resulta resultb
seq 4 | tee  >(tac > resulta) >(awk '$1*=2' > resultb) | paste -d ' ' - resulta resultb
rm resulta resultb
1 4 2
2 3 4
3 2 6
4 1 8

答案2

yash外壳具有独特的功能(管道重定向进程重定向)让事情变得更容易:

cat myfile | (
  exec 3>>|4
  tee /dev/fd/5 5>(commanda >&3 3>&-) 3>&- |
    commandb 3>&- |
    paste -d , /dev/fd/4 - 3>&-
)

3>>|4管道重定向) 创建一个管道,其中写入端位于 fd 3 上,读取端位于 fd 4 上。

3>(commanda>&3)进程重定向,有点像 ksh/zsh/bash 进程替换,但只是进行重定向,而不替换为/dev/fd/n. ksh's>(cmd)或多或少与yash's相同n>(cmd) /dev/fd/n(有一个您无法控制的n文件描述符选择)。ksh

答案3

zsh

pee() (
  n=0 close_in= close_out= inputs=() outputs=()
  merge_command=$1; shift
  for cmd do
    eval "coproc $cmd $close_in $close_out"

    exec {i}<&p {o}>&p
    inputs+=($i) outputs+=($o)
    eval i$n=$i o$n=$o
    close_in+=" {i$n}<&-" close_out+=" {o$n}>&-"
    ((n++))
  done
  coproc :
  read -p
  eval tee /dev/fd/$^outputs $close_in "> /dev/null &
    " exec $merge_command /dev/fd/$^inputs $close_out
)

然后用作:

$ echo abcd | pee 'paste -d,' 'tr a A' 'tr b B' 'tr c C'
Abcd,aBcd,abCd

这是改编自这另一个问题您将在其中找到一些详细的解释和限制提示(小心死锁!)。

答案4

对于您的特定示例,应该不需要paste其余的。通常,当我们遇到标准工具集的限制时,这是因为我们想要用一种方式做的事情可以用另一种方式来做。例如:

set 1 2 3 4
while [ "$#" -gt 0 ]
do    echo "$1" "$#" "$(($1*2))"
shift;done

...打印...

1 4 2
2 3 4
3 2 6
4 1 8

您可以获得一个包含您在 shell"$@"数组中提到的内容的文件,例如...

set -f; IFS='
'; set -- $(cat)

要验证像上面这样的循环中的 arg 值,您可以稍微更改初始测试......

while { [ "$1" -eq "${1-1}" ] ;} 2>&"$((2+!$#))"
do    echo "$1" "$#" "$(($1*2))"
shift;done  3>/dev/null >outfile

set -- $(cat)...仅当读入的行包含不完全由单个整数组成的行时,才会向 stderr 打印错误。

相关内容