Rendere subordinata una pipe al ritorno non vuoto


Risposte:


9

La seguente ifnotemptyfunzione 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 --fooin sink --barscrivendo 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:

  • Mi aspetto che questa implementazione funzioni su tutte le piattaforme POSIX / Unix, anche se a rigor di termini non è conforme agli standard: si basa sul ddnon leggere più di un byte che viene letto sul suo input standard.
  • Penso che head -c 1sarebbe un sostituto adatto per dd bs=1 count=1 2>/dev/nullsu Linux.
  • D'altra parte, head -n 1non sarebbe adatto perché in headgenere 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 heade anche read -r -n 1 headqui non sono adatti perché se il primo carattere è una nuova riga, headverrebbe impostato sulla stringa vuota, rendendo impossibile distinguere tra input vuoto e input iniziando con una riga vuota.
  • Non possiamo semplicemente scrivere 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.
  • In bash, ksh o zsh, è possibile sostituirlo catcon </dev/stdinun 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:

  • I dati prodotti dalla fonte sono considerati vuoti se e solo se sono costituiti esclusivamente da caratteri di nuova riga. (Questo può in effetti essere desiderabile.)
  • I dati immessi nel sink terminano esattamente con un carattere di nuova riga, indipendentemente da quante nuove righe terminano i dati prodotti dalla sorgente. (Questo potrebbe essere un problema.)

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
}

17

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.


4

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
$

Questa funzione non riesce per me quando corro echo -en "\nX" | pipe_if_not_empty mail -s "Subject line here" foo@bar.com. Lo pensa linee heresono entrambi destinatari dell'e-mail, non token nell'oggetto. Devo scappare "dall'argomento circostante per farlo funzionare. Tuttavia, la pipe_if_not_emptyfunzione della risposta accettata funziona per me anche senza sfuggire a nulla.
kuzzooroo,

2

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


yourothercommandnon vede mai l'output di yourcommand.
In pausa fino a nuovo avviso.

2

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
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.