Dividi un input per un comando diverso e combina il risultato


8

So come combinare il risultato di un comando diverso

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

Conosco lo stesso input per comandi diversi

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

Ora come combinare questi comandi? In modo che io possa fare

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

Di 'che ho un file

il mio file:

1
2
3
4

Voglio fare un nuovo file

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

ero solito

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

mi darebbe un risultato in verticale, dove voglio davvero incollarli in ordine orizzontale.


potrebbe essere necessario scrivere due flussi per separare le pipe dei nomi e combinarle con un programma di monitoraggio.
把 友情 留 在 无 盐

Risposte:


8

Quando ti colleghi a più sostituzioni di processo, non sei sicuro di ottenere l'output in un ordine particolare, quindi è meglio attenersi a

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

Supponendo che cat myfilesi tratti di una pipeline costosa, penso che dovrai archiviare l'output, in un file o in una variabile:

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

Usando il tuo esempio:

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

Un altro pensiero: FIFO e un'unica conduttura

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

posso usare sth come / dev / fd / 3-9?
user40129

4

La yashshell ha caratteristiche uniche ( reindirizzamento della pipeline e reindirizzamento del processo ) che rendono più semplice lì:

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

3>>|4( reindirizzamento della pipeline ) crea una pipe in cui la fine della scrittura è su fd 3 e la fine della lettura su fd 4.

3>(commanda>&3)è il reindirizzamento del processo , un po 'come la sostituzione del processo di ksh / zsh / bash ma fa semplicemente il reindirizzamento e non sostituisce con /dev/fd/n. ksh's >(cmd)è più o meno uguale a yash' s n>(cmd) /dev/fd/n(c'è nun descrittore di file scelto da kshcui non hai alcun controllo).


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
)

Quindi utilizzare come:

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

È adattato da questa altra domanda in cui troverai alcune spiegazioni dettagliate e suggerimenti sulle limitazioni (attenzione ai deadlock!).


0

Per il tuo esempio particolare non dovrebbe esserci bisogno pastee il resto. È spesso vero che quando incontriamo un limite con il set di strumenti standard è perché ciò che vogliamo fare in un modo può essere fatto in un altro. Ad esempio:

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

... che stampa ...

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

È possibile ottenere un file con contenuti come quelli menzionati "$@"nell'array di shell come ...

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

E per convalidare i valori arg in un ciclo come quello sopra puoi cambiare un po 'il test iniziale ...

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

... che stampa un errore su stderr solo se una riga letta con set -- $(cat)contiene una riga che non è interamente costituita da un singolo numero intero.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.