В Bash Tee делает переменные функции локальными, как мне избежать этого?

Я застрял с Bash Scipt, который должен писать как в стандартный вывод и в файл. Я использую функции и некоторые переменные внутри них. Всякий раз, когда я пытаюсь перенаправить функцию в файл и печатать на экране, я не могу использовать переменные, которые я использовал в функции, поэтому они как-то становятся локальными. Вот простой пример:

#!/bin/bash
LOGV=/root/log

function var()
{
echo -e "Please, insert VAR value:\n"
read -re VAR
}
var 2>&1 | tee $LOGV
echo "This is VAR:$VAR"

Выход:

[root@testbox ~]# ./var.sh   
Please, insert VAR value:

foo
This is VAR:
[root@testbox ~]#

Заранее спасибо!

РЕДАКТИРОВАТЬ: Ответ на предложение @Etan Reisner использоватьvar 2>&1 > >(tee $LOGV)

Единственная проблема этой конструкции в том, что файл журнала не получает все...

[root@testbox~]# ./var.sh
Please, insert VAR value: 

foo 
This is VAR:foo
[root@testbox ~]# cat log 
Please, insert VAR value:

1 ответ

Решение

Это вариант BashFAQ # 24.

var 2>&1 | tee $LOGV

... как и любой конвейер оболочки, имеет возможность запустить функцию var внутри подпроцесса - и на практике ведет себя таким образом в bash. (Спецификация POSIX sh оставляет детали того, какие компоненты конвейера, если таковые имеются, выполняются внутри родительской оболочки, не определены).


Избежать этого так же просто, как не использовать конвейер.

var > >(tee "$LOGV") 2>&1

... использует подстановку процесса (расширение ksh, принятое bash, отсутствует в POSIX sh) для представления tee подпроцесс через имя файла (в виде /dev/fd/## в современном Linux), к которому можно перенаправить вывод без перемещения функции в конвейер.


Если вы хотите убедиться, что tee завершает работу до запуска других команд, используйте блокировку:

#!/bin/bash
logv=/tmp/log

collect_var() {
        echo "value for var:"
        read -re var
}
collect_var > >(logv="$logv" flock "$logv" -c 'exec tee "$logv"') 2>&1
flock "$logv" -c true # wait for tee to exit

echo "This is var: $var"

Между прочим, если вы хотите запустить несколько команд так, чтобы их вывод передавался таким образом, вы должны вызвать tee только один раз, и подайте в него соответственно:

#!/bin/bash
logv=/tmp/log
collect_var() { echo "value for var:"; read -re var; }

exec 3> >(logv="$logv" flock "$logv" -c 'exec tee "$logv"') # open output to log
collect_var >&3 2>&3         # run function, sending stdout/stderr to log
echo "This is var: $var" >&3 # ...and optionally run other commands the same way
exec 3>&-                    # close output
flock "$logv" -c true        # ...and wait for tee to finish flushing and exit.
Другие вопросы по тегам