Faça um pipe condicional no retorno não vazio

15

Existe uma maneira de enviar a saída de uma função para outra, mas somente se for uma saída?

    
por Tgr 12.11.2010 / 10:49

6 respostas

9

A seguinte função ifnotempty canaliza sua entrada para o comando passado como um argumento, exceto que não faz nada se a entrada estiver vazia. Use-o para canalizar source --foo para sink --bar escrevendo 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
}

Notas de design:

  • Espero que esta implementação funcione em todas as plataformas POSIX / Unix, embora, estritamente falando, não seja compatível com os padrões: ela depende de dd não ler mais do que o byte que é contado para ler em sua entrada padrão. / li>
  • Acho que head -c 1 seria um substituto adequado para dd bs=1 count=1 2>/dev/null no Linux.
  • Por outro lado, head -n 1 não seria adequado porque head normalmente armazena em buffer sua entrada e pode ler mais do que a linha que ela gera - e como está lendo de um canal, os bytes extras são perdidos. / li>
  • read -r head e mesmo read -r -n 1 head não são adequados aqui porque se o primeiro caractere for uma nova linha, head será definido como uma string vazia, impossibilitando a distinção entre entrada vazia e entrada começando com uma linha em branco.
  • Não podemos simplesmente escrever head=$(head -c 1) , porque se o primeiro caractere for uma nova linha, a substituição de comandos removerá a nova linha final, tornando impossível distinguir entre entrada vazia e entrada começando com uma linha em branco.
  • No bash, ksh ou zsh, você pode substituir cat por </dev/stdin para um ganho de desempenho microscópico.

Se você não se importar em armazenar todos os dados intermediários na memória, aqui está uma implementação um pouco mais simples de pipe_if_not_empty .

pipe_if_not_empty () {
  input=$(cat; echo a);
  if [ "x$input" != x"a" ]; then
    { printf %s "${input%a}"; } | "$@"
  fi
}

Aqui está uma implementação um pouco mais simples com as seguintes ressalvas:

  • Os dados produzidos pela fonte são considerados vazios se, e somente se, consistirem apenas em caracteres de nova linha. (Isso pode de fato ser desejável).
  • Os dados alimentados no coletor terminam com exatamente um caractere de nova linha, independentemente de quantas novas linhas finais os dados produzidos pela origem terminam. (Isso pode ser um problema.)

Novamente, todos os dados são armazenados na memória.

pipe_if_not_empty () {
  input=$(cat);
  if [ "x$input" != x"" ]; then
    { printf '%s\n' "${input}"; } | "$@"
  fi
}
    
por 12.11.2010 / 15:42
13

Isso deve funcionar para você

$ --a function-- | [ xargs -r ] --another function--

Um exemplo

$ 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

É simples, mas deve funcionar para você. Se a sua "função" enviar uma string vazia ou até mesmo uma nova linha no pipeline, o comando xargs -r impedirá a passagem para "outra função".

Referência para xargs: link

-r, --no-run-if-empty
Do not run command if standard input contains only blanks.
    
por 21.02.2013 / 23:31
9

ifne (1) de < href="http://kitenet.net/~joey/code/moreutils/"> moreutils faz exatamente isso. Moreutils está disponível como um pacote pelo menos no Debian e no Ubuntu, provavelmente em outras distribuições também.

    
por 02.01.2011 / 15:36
4

A função abaixo tenta ler o primeiro byte e, se houver ecos bem-sucedidos, esse byte e o resto do gato. Deve ser eficiente e 100% portátil.

if_read() {
    IFS="" read -rN 1 BYTE && { echo -nE "$BYTE"; cat; } | "$@";
}

Casos de teste:

$ echo -n | if_read wc -c
$ echo | if_read wc -c
1
$ echo -en "\nX" | if_read wc -c
2
$
    
por 20.10.2012 / 22:37
2

Em vez de sender | receiver :

tester () { local a=$(</dev/stdin); if [[ $a ]]; then printf '%s\n' "$a" | receiver; fi; }
sender | tester

Ou você poderia torná-lo mais genérico, alterando-o para aceitar o programa de recebimento como um argumento, como na resposta de Gilles:

tester () { local a=$(</dev/stdin); if [[ $a ]]; then printf '%s\n' "$a" | "$@"; fi; }
sender | tester receiver
    
por 12.11.2010 / 13:10
1

Pelo menos, algo assim funciona:

yourcommand | if [ $(wc -c) -gt "0" ]; then yourothercommand; fi

Por favor, note que o acima irá considerar feeds de linha e outros caracteres especiais como saída, então uma linha vazia passada para essa declaração if será considerada como uma saída. Apenas aumente o limite -gt se sua saída deve ser maior que 1 byte:)

    
por 12.11.2010 / 11:01

Tags