2010-07-17 13 views
6

Sto ricevendo un flusso di numeri in una pipe, e vorrei eseguire alcune operazioni prima di passarle alla sezione successiva, ma sono un po 'perso su come Ci andrei senza rompere la pipa.BASH: come eseguire aritmetica sui numeri in una pipe

ad esempio

> echo "1 2 3 4 5" | some command | cat 
1 4 9 16 25 
> 

Vuoi avere qualche idea su come fare qualcosa di simile a questo lavoro? L'operazione effettiva che voglio eseguire è semplicemente aggiungendo uno ad ogni numero.

risposta

7
echo 1 2 3 4 5|{ 
    read line; 
    for i in $line; 
    do 
    echo -n "$((i * i)) "; 
    done; 
    echo 
} 

Il {} crea un raggruppamento. Potresti invece creare uno script per questo.

+0

Grazie Matteo! Esattamente quello che stavo cercando. In retrospettiva, avrei dovuto essere in grado di capirlo da solo però :-p – brice

+0

Non hai bisogno di una subshell. Sostituisci la tua subshell inutile, brutta e inefficiente con un raggruppamento '{...}'. –

+0

Per quelli confusi dal commento di @gniourf_gniourf, si applicava alla risposta non modificata che ora è stata modificata per includere il raggruppamento '{}'. –

0

Se preferite Python:

#!/bin/python 
num = input() 
while num: 
    print(int(num) + 1) # Whatever manipulation you want 
    try: 
     num = input() 
    except EOFError: 
     break 
3

O ..

echo "1 2 3 4 5" | xargs -n 1 | while read number 
do 
    echo $((number * number)) 
done 
+0

Grazie lasseoe. Per metterlo in mezzo alla pipa, avrei bisogno di metterlo anche in una sottotazza. Il tuo non fa cadere i numeri però, che è meglio! – brice

+0

In realtà, qualsiasi comando di shell in una pipe viene eseguito automaticamente in una subshell; l'unica ragione per metterlo in '()' sarebbe raggrupparlo con un finale 'echo' come faceva Matthew Flaschen, e anche lì si potrebbe usare anche '{}' (che raggruppa senza forzare una subshell). –

1

Oppure è possibile tubo per l'espressione di bc:

echo "1 2 3 4 5" | (
    read line; 
    for i in $line; 
    do 
    echo $i^2 | bc; 
    done; 
    echo 
) 
4

mi piacerebbe scrivere:

echo "1 2 3 4 5" | { 
    for N in $(cat); do 
    echo $((N ** 2)) 
    done | xargs 
} 

Possiamo pensarlo come una "mappa" (programmazione funzionale). Ci sono un sacco di modi di scrivere una funzione "mappa" in bash (usando stdin, args funzione, ...), per esempio:

map_stdin() { 
    local FUNCTION=$1 
    while read LINE; do 
    $FUNCTION $LINE 
    done 
} 

square() { echo "$(($1 * $1))"; } 

$ echo "1 2 3 4 5" | xargs -n1 | map_stdin square | xargs 
1 4 9 16 25 
1
echo 1 2 3 4 5 | xargs -n 1 bash -c 'echo $(($1*$1))' args 
1
echo 1 2 3 4 5 | xargs -n 1 expr -1 + 
+0

Questo produce solo i 5 numeri per me. – Tim

-1

Yoi potrebbe piacere qualcosa di simile:

echo "1 2 3 4 5" | perl -ne 'print $_ ** 2, " " for split//, $_' 

e neppure come questo:

echo "1 2 3 4 5" | perl -ne 'print join " ", map {$_ ** 2} split//, $_' 
1

Utilizzando awk è un'altra soluzione abbastanza compatto

echo "1 2 3 4 5" | xargs -n1 | awk '{print $1**2}' | xargs 
0

xargs, xargs, xargs

echo 1 2 3 4 5 | xargs -n1 echo | xargs -I NUMBER expr NUMBER \* NUMBER | xargs 

Oppure andare in parallelo:

squareit() { expr $1 \* $1; } 
export -f squareit 
echo 1 2 3 4 5 | xargs -n1 | parallel --gnu squareit | xargs 

Quale sarebbe modo più semplice se hai superato la pipa come un set standard di args:

parallel --gnu "expr {} \* {}" ::: $(echo 1 2 3 4 5) | xargs 

O anche:

parallel --gnu "expr {} \* {}" ::: 1 2 3 4 5 | xargs 

Davvero vale la pena dare un'occhiata agli esempi nella doc: https://www.gnu.org/software/parallel/man.html