Подтвердить что ты не робот

Записать стандартный вывод в переменную, но по-прежнему отображать его в консоли

У меня есть bash script, который вызывает несколько длительных процессов. Я хочу записать вывод этих вызовов в переменные для обработки. Однако, поскольку это длительные процессы, я хотел бы, чтобы вывод вызовов rsync отображался в консоли в режиме реального времени, а не после факта.

С этой целью я нашел способ сделать это, но он полагается на вывод текста в /dev/stderr. Я чувствую, что вывод в /dev/stderr - не лучший способ сделать что-то.

VAR1=$(for i in {1..5}; do sleep 1; echo $i; done | tee /dev/stderr)

VAR2=$(rsync -r -t --out-format='%n%L' --delete -s /path/source1/ /path/target1 | tee /dev/stderr)

VAR3=$(rsync -r -t --out-format='%n%L' --delete -s /path/source2/ /path/target2 | tee /dev/stderr)

В приведенном выше примере я вызываю rsync несколько раз, и я хочу видеть имена файлов по мере их обработки, но в конце я все еще хочу получить результат в переменной, потому что я буду разбирать его позже.

Есть ли "более чистый" способ сделать это?

Если это имеет значение, я использую Ubuntu 12.04, bash 4.2.24.

4b9b3361

Ответ 1

Дублируйте & 1 в своей оболочке (в моем примере до 5) и используйте & 5 в подоболочке (так что вы будете писать в stdout (& 1) родительской оболочки):

exec 5>&1
FF=$(echo aaa|tee >(cat - >&5))
echo $FF

Будет печатать aaa два раза, из-за эха в подоболочке и второй раз печатать значение переменной.

В вашем коде:

exec 5>&1
VAR1=$(for i in {1..5}; do sleep 1; echo $i; done | tee >(cat - >&5))
# use the value of VAR1

Ответ 2

Ответ Op De Cirkel имеет правильную идею. Его можно упростить еще больше (избегая использования cat):

exec 5>&1
FF=$(echo aaa|tee /dev/fd/5)
echo $FF

Ответ 3

Здесь пример, содержащий как stderr, так и код выхода команды. Это строится на ответе Рассела Дэвиса.

exec 5>&1
FF=$(ls /taco/ 2>&1 |tee /dev/fd/5; exit ${PIPESTATUS[0]})
exit_code=$?
echo "$FF"
echo "Exit Code: $exit_code"

Если папка /taco/ существует, это будет захватывать ее содержимое. Если папка не существует, она будет отображать сообщение об ошибке, а код выхода будет равен 2.

Если вы опустите 2>&1, тогда будет снято только stdout.

Ответ 4

Вы можете использовать более трех файловых дескрипторов. Попробуйте здесь:

http://tldp.org/LDP/abs/html/io-redirection.html

"Каждому открытому файлу присваивается дескриптор файла. [2] Дескрипторы файлов для stdin, stdout и stderr равны 0, 1 и 2 соответственно. Для открытия дополнительных файлов остаются дескрипторы с 3 по 9. Иногда полезно назначьте один из этих дополнительных файловых дескрипторов stdin, stdout или stderr как временную дублирующую ссылку. "

Дело в том, стоит ли усложнять сценарий только для достижения этого результата. На самом деле это не совсем так, как вы это делаете.

Ответ 5

Если под "консолью" вы подразумеваете свой текущий TTY, попробуйте

variable=$(command with options | tee /dev/tty)

Это немного сомнительная практика, потому что люди, которые пытаются использовать это, иногда удивляются, когда результат попадает куда-то неожиданно, когда у них нет TTY (задания cron и т.д.).

Ответ 6

В качестве альтернативы использованию /dev/tty или дополнительного файлового дескриптора, как предлагают другие ответы, вы также можете перевернуть его и просто использовать временный файл. Это, возможно, легче читать и более переносимо в определенных ситуациях.

tmpFile=$(mktemp)  # mak-a de temp
rsync /a /b | tee $tmpFile # sync my b*tch up
if grep "U F'd up" $tmpFile; then
  rm -rf / #Seppuku
fi