Dividir una entrada para un comando diferente y combinar el resultado.

8

Sé combinar el resultado de un comando diferente.

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

Sé canalizar la misma entrada a un comando diferente

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

¿Ahora cómo combinar estos comandos? Para que yo pueda hacer

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

Digamos que tengo un archivo

mi archivo:

1
2
3
4

Quiero hacer un nuevo archivo

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

solía

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

me daría un resultado vertical, donde realmente quiero pegarlos en orden horizontal.

usuario40129
fuente
Puede que tenga que escribir dos secuencias para separar las canalizaciones de nombre y combinarlas con un programa de monitor.
把 友情 留 在 无 盐

Respuestas:

8

Cuando haces tee con múltiples sustituciones de proceso, no tienes la garantía de obtener el resultado en un orden particular, por lo que es mejor que te quedes con

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

Suponiendo que cat myfilerepresenta una tubería costosa, creo que tendrá que almacenar la salida, ya sea en un archivo o una variable:

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

Usando tu ejemplo:

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

Otro pensamiento: FIFOs, y una sola tubería

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
Glenn Jackman
fuente
¿Puedo usar algo como / dev / fd / 3-9?
user40129
4

El yashshell tiene características únicas ( redirección de canalización y redirección de proceso ) que lo hacen más fácil allí:

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

3>>|4( redirección de canalización ) crea una canalización donde el final de escritura está en fd 3 y el final de lectura en fd 4.

3>(commanda>&3)es la redirección de procesos , un poco como la sustitución de procesos ksh / zsh / bash, pero solo realiza la redirección y no la sustituye por /dev/fd/n. ksh's >(cmd)es más o menos lo mismo que yash' s n>(cmd) /dev/fd/n(hay nun descriptor de archivo elegido por kshel cual no tiene control).

Stéphane Chazelas
fuente
3

Con 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
)

Luego use como:

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

Eso está adaptado de esta otra pregunta donde encontrará algunas explicaciones detalladas y sugerencias sobre las limitaciones (¡cuidado con los puntos muertos!).

Stéphane Chazelas
fuente
0

Para su ejemplo particular, no debería haber necesidad pastey el resto. A menudo es cierto que cuando encontramos un límite con el conjunto de herramientas estándar es porque lo que queremos hacer de una manera se puede hacer de otra manera. Como:

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

... que imprime ...

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

Puede obtener un archivo con el contenido que menciona en su "$@"matriz de shell como ...

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

Y para validar los valores arg en un bucle como el anterior, puede cambiar un poco la prueba inicial ...

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

... que imprime un error en stderr solo si una línea leída set -- $(cat)contiene una línea que no consiste completamente en un solo entero.

mikeserv
fuente