Solução elegante para ecoar ou stdout ou file em bash

0

Eu tenho um aplicativo bash que está produzindo algum resultado e gostaria de fazer o eco do resultado para stdout ou para um arquivo escolhido pelo usuário. Porque eu também faço eco de outras mensagens interativas indo para a tela, exigindo que o usuário use explicitamente o redirecionamento > quando ele quiser fazer o eco do resultado para um arquivo não é uma opção (*), pois essas mensagens também apareceriam no arquivo .

Agora eu tenho uma solução, mas é feia.

if [ -z $outfile ]
then
    echo "$outbuf"    # Write output buffer to the screen (stdout)
else
    echo "$outbuf" > $outfile  # Write output buffer to file
fi

Eu tentei fazer com que a variável $outfile fosse igual a stdout , a &1 e talvez outra coisa, mas apenas escrevesse para um arquivo com esse nome e não para stdout. Existe uma solução mais elegante?

(*) Eu poderia trapacear e usar stderr para esse propósito, mas acho que também é bem feio, não é?

    
por Bregalad 10.01.2017 / 10:33

3 respostas

4

Primeiro, você deve evitar que echo envie dados arbitrários .

Em sistemas diferentes dos baseados em Linux, você pode usar:

logfile=/dev/stdout

Para Linux, isso funciona para alguns tipos de stdout, mas falha quando stdout é um soquete ou pior, se stdout for um arquivo regular, truncaria esse arquivo em vez de gravar na posição atual que o stdout está no arquivo.

Além disso, no shell tipo Bourne, não há como ter o redirecionamento condicional , embora você possa usar eval :

eval 'printf "%s\n" "$buf" '${logfile:+'> "$logfile"'}

Em vez de uma variável , você poderia usar um descritor de arquivos :

exec 3>&1
[ -z "$logfile" ] || exec 3> "$logfile"

 printf '%s\n' "$buf" >&3

Uma desvantagem (pequena) com isso é que, exceto em ksh , o fd3 seria vazado para todos os comandos executados no script. Com zsh , você pode fazer sysopen -wu 3 -o cloexec -- "$logfile" || exit no lugar de exec 3> "$logfile" , mas bash não tem equivalente.

Outro idioma comum é usar uma função como:

log() {
  if [ -n "$logfile" ]; then
    printf '%s\n' "$@" >> "$logfile"
  else
    printf '%s\n' "$@"
  fi
}

log "$buf"
    
por 10.01.2017 / 11:45
4
  1. Defina outfile para "/dev/stdout" .
  2. Deixe o usuário escolher o nome do arquivo, substituindo outfile ou mantendo o valor padrão.
  3. printf '%s\n' "$outbuf" >"$outfile"

Eu usei printf devido a " Por que o printf é melhor que o eco? ".

Para advertências a esta solução, veja a resposta de Stéphane Chazelas .

    
por 10.01.2017 / 11:25
0

Tente isso

#!/bin/bash
TOSCREEN="1" # empty OR 0 to log to file
if [[ ! -z "$TOSCREEN" && $TOSCREEN == "1" ]]; then
    echo "$outbuf"    # Write output buffer to the screen (stdout)
else
    if [[ ! -f $outfile ]]; then
      #echo -e "Making file "
      touch "$outfile"
    fi  
    echo "$outbuf" >> $outfile  # Write output buffer to file
fi
    
por 10.01.2017 / 11:21