C'è un modo per convogliare l'output di una funzione all'altra, ma solo se v'è un'uscita?
C'è un modo per convogliare l'output di una funzione all'altra, ma solo se v'è un'uscita?
Risposte:
La seguente ifnotempty
funzione reindirizza il suo input al comando passato come argomento, tranne per il fatto che non fa nulla se l'input è vuoto. Usalo per tubi source --foo
in sink --bar
scrivendo source --foo | pipe_if_not_empty sink --bar
.
pipe_if_not_empty () {
head=$(dd bs=1 count=1 2>/dev/null; echo a)
head=${head%a}
if [ "x$head" != x"" ]; then
{ printf %s "$head"; cat; } | "$@"
fi
}
Note di progettazione:
dd
non leggere più di un byte che viene letto sul suo input standard.head -c 1
sarebbe un sostituto adatto per dd bs=1 count=1 2>/dev/null
su Linux.head -n 1
non sarebbe adatto perché in head
genere buffer il suo input e potrebbe leggere più di una riga che emette - e poiché sta leggendo da una pipe, i byte extra vengono semplicemente persi.read -r head
e anche read -r -n 1 head
qui non sono adatti perché se il primo carattere è una nuova riga, head
verrebbe impostato sulla stringa vuota, rendendo impossibile distinguere tra input vuoto e input iniziando con una riga vuota.head=$(head -c 1)
perché se il primo carattere è una nuova riga, la sostituzione del comando eliminerebbe la nuova riga finale, rendendo impossibile distinguere tra input vuoto e input iniziando con una riga vuota.cat
con </dev/stdin
un guadagno microscopico delle prestazioni.Se non ti dispiace memorizzare tutti i dati intermedi in memoria, ecco un'implementazione leggermente più semplice di pipe_if_not_empty
.
pipe_if_not_empty () {
input=$(cat; echo a);
if [ "x$input" != x"a" ]; then
{ printf %s "${input%a}"; } | "$@"
fi
}
Ecco un'implementazione leggermente più semplice con le seguenti avvertenze:
Ancora una volta, tutti i dati vengono archiviati in memoria.
pipe_if_not_empty () {
input=$(cat);
if [ "x$input" != x"" ]; then
{ printf '%s\n' "${input}"; } | "$@"
fi
}
Questo dovrebbe funzionare per te
$ --a function-- | [ xargs -r ] --another function--
Un esempio
$ echo -e "\n\n" | xargs -r ls
$ # No output. ls did not run.
$ echo -e "\n\n1" | xargs -r ls
ls: cannot access 1: No such file or directory
È semplice ma dovrebbe funzionare per te. Se la tua "funzione" invia una stringa vuota o addirittura una nuova riga lungo la pipeline, xargs -r, impedirà il passaggio a "un'altra funzione".
Riferimento per xargs: http://www.oreillynet.com/linux/cmd/cmd.csp?path=x/xargs
-r, --no-run-if-empty
Do not run command if standard input contains only blanks.
La funzione seguente tenta di leggere il 1 ° byte e, in caso di successo, riecheggia quel byte e gatta il resto. Dovrebbe essere efficiente e portatile al 100%.
if_read() {
IFS="" read -rN 1 BYTE && { echo -nE "$BYTE"; cat; } | "$@";
}
Casi test:
$ echo -n | if_read wc -c
$ echo | if_read wc -c
1
$ echo -en "\nX" | if_read wc -c
2
$
echo -en "\nX" | pipe_if_not_empty mail -s "Subject line here" foo@bar.com
. Lo pensa line
e here
sono entrambi destinatari dell'e-mail, non token nell'oggetto. Devo scappare "
dall'argomento circostante per farlo funzionare. Tuttavia, la pipe_if_not_empty
funzione della risposta accettata funziona per me anche senza sfuggire a nulla.
Almeno qualcosa del genere funziona:
yourcommand | if [ $(wc -c) -gt "0" ]; then yourothercommand; fi
Si noti che quanto sopra considererà feed di riga e altri caratteri speciali come output, quindi una riga vuota viene passata a tale se l'istruzione verrà considerata come output. Aumenta il limite -gt se l'output dovrebbe essere in genere superiore a 1 byte :)
yourothercommand
non vede mai l'output di yourcommand
.
Invece di sender | receiver
:
tester () { local a=$(</dev/stdin); if [[ $a ]]; then printf '%s\n' "$a" | receiver; fi; }
sender | tester
Oppure potresti renderlo più generale modificandolo per accettare il programma di ricezione come argomento come nella risposta di Gilles:
tester () { local a=$(</dev/stdin); if [[ $a ]]; then printf '%s\n' "$a" | "$@"; fi; }
sender | tester receiver