Redirecionando o stdout para terminal e arquivo sem usar um pipe?

4

Eu tenho código, algo assim:

#!/bin/bash

VAR=0

func() {
    VAR=$((VAR+1))
    echo 'Logging information.'
}

func 2>&1 | tee 'log.txt'

echo "Should be 1: ${VAR}"

Ao ligar, isso acontece:

:~$ ./script.sh
Should be 1: 0

Tanto quanto eu entendo, isso é porque o pipe que estou usando está gerando um subshell. As alterações em VAR não são propagadas e, portanto, não são refletidas na saída.

func no meu caso é um processo demorado e a saída precisaria estar em tempo real. Portanto, apenas gravar em um arquivo e, em seguida, cat ing o arquivo não é uma opção. Também gostaria de evitar escrever qualquer coisa para arquivar e depois lê-lo novamente como uma Variável, se possível.

Então, há alguma maneira de obter a necessidade do cano de lá, ou um truque básico que eu ainda não conheço, que poderia me ajudar?

EDITAR:

Tentei com um pipe nomeado:

#!/bin/bash

VAR=0

func() {
        VAR=$((VAR+1))
        echo 'Logging information.'
        sleep 5
}

mkfifo my_fifo

func >my_fifo 2>&1 &

tee 'log .txt' <my_fifo

echo "Should be 1: ${VAR}"

O resultado é infelizmente o mesmo:

:~$ ./script.sh
Should be 1: 0
    
por Minix 18.12.2014 / 13:32

3 respostas

3

Você pode fazer algo como:

func > >(tee log.txt) 2>&1
wait

Você pode dedicar um descritor de arquivo para registro:

exec 3> >(tee log.txt)
tee_pid=$!

func >&3 2>&1
...

Entretanto, tenha em mente que tee é executado em segundo plano, se nem toda a saída passar por ele, então a ordem na saída pode ser afetada.

    
por 18.12.2014 / 15:12
1

Você pode usar um arquivo tmp

func >tmpfile 2>&1
tee 'log.txt' <tmpfile

ou um FIFO

mkfifo pipe_replacement
tee 'log.txt' <pipe_replacement &
func >pipe_replacement 2>&1
    
por 18.12.2014 / 13:37
1

Eu faria como ...

#!/bin/sh -x
run() if ! ps -p "$run" >&2
      then n=0 run=$$ exec "$0" "$@" 2>&1 | {
         ! tee outfile ; }
      fi 2>/dev/null
run "$@" || exit

fn() { var=val$n; echo "$((n+=1)): $var"; }
fn 
sleep 5
fn
IN

Isso primeiro verifica se ele já tem um canal aberto para um tee em outro processo e, se não, o exec s em um. Depois disso, toda a saída é canalizada para o restante do script, e todas as variáveis definidas entre elas e a saída do script são mantidas - o que é, de fato, como a verificação é feita em primeiro lugar.

Então, executando as impressões acima:

+ run
+ run
+ fn
+ var=val0
+ echo 1: val0
1: val0
+ sleep 5
+ fn
+ var=val1
+ echo 2: val1
2: val1
+ exit

E a execução de cat outfile depois é impressa ...

+ run
+ run
+ fn
+ var=val0
+ echo 1: val0
1: val0
+ sleep 5
+ fn
+ var=val1
+ echo 2: val1
2: val1
+ exit

Você também pode considerar usar sed em vez de tee . Enquanto tee terá mais desempenho para um caso todas as saídas , sed pode gravar em muitos arquivos de uma só vez e pode fazê-lo condicionalmente.

Por exemplo, você pode fazer eco de uma linha como:

echo 'LOG-ONLY: some message here'

E um processo de sed de audição poderia ser feito:

sed -n '/^LOG-ONLY:/!p;s///w ./my_log.txt'

... o que seria w desta linha para um arquivo depois de remover a parte LOG_ONLY e evitar imprimi-la no terminal.

    
por 18.12.2014 / 17:19