переключить перенаправление выходного файла в середине bash-скрипта - PullRequest
0 голосов
/ 16 октября 2018

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

#!/bin/bash

# Here I don't know where to write outputfile so I create a tmpfile
fic=$(mktemp -t)
trap 'rm -f $fic' EXIT
rm -f $fic
:> $fic
exec 3<> $fic
exec 1>&3
exec 2>&3

# some code in particular reading options and dest dir
echo foo
dir="."

# Here I finally know where I can write my output
fic2=$dir/log.log

cp -f $fic $fic2
exec 3>&- # close fd #3
exec 1>> $fic2
exec 2>&1

echo bar

Кроме того, я хотел бы показать весь вывод, что-то вроде $ exec ...> (tee $ fic) $, но мне не удалось найти решение.

Большое спасибо за любые советы.PJLM

1 Ответ

0 голосов
/ 17 октября 2018

Если вы знаете, что оба выходных файла находятся в одной файловой системе , вы можете просто mv файл вывода.Открытые дескрипторы файлов будут продолжать работать.

exec 1>/tmp/out1 2>&1
echo out1
mv /tmp/out1 /tmp/out2   # replace with your desired destination
echo out2

Если вы хотите tee выводить данные, и, опять же, оба выходных файла находятся в одной файловой системе, вы можете сделать довольнопочти то же самое (когда tee открыл файл для записи, он также продолжит запись на тот же файл, даже если файл перемещается).

log1=$(mktemp)
exec 3>"$log1"
exec 1> >(tee /dev/fd/3) 2>&1
echo out1
mv "$log1" "$log2"
echo out2

Обратите внимание, что вместо выполнения >(tee "$log1") Сначала я открываю fd 3 в оболочке, а затем использую >(tee /dev/fd/3).Это потому, что в противном случае существует потенциальное состояние гонки, при котором tee не откроет файл к тому времени, когда мы перейдем к шагу mv.(exec ожидает только, пока не запускается subshell , в котором будет запускаться tee, но для запуска и открытия файла tee потребуется некоторое время).


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

В случае простого перенаправления нам нужно закрыть файловые дескрипторы перед перемещением:

exec 1>"$log1" 2>&1
echo out1
exec 1>&- 2>&-
mv "$log1" "$log2"
exec 1>>"$log2" 2>&1
echo out2

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

exec 3>&1 4>&2                # save original stdout, stderr
exec 1> >(tee "$log1") 2>&1   # redirect to tee
pid=$!                        # save pid of tee's subshell

echo out1
exec 1>&3 2>&4                # restore original stdout, stderr

# wait until tee is done. on newer bash can use `wait $pid` instead
while kill -0 $pid 2>/dev/null; do :; done

mv "$log1" "$log2"

# repeat steps above for new file
exec 3>&1 4>&2
exec 1> >(tee -a "$log2") 2>&1
pid=$!
echo out2
exec 1>&3 2>&4
while kill -0 $pid 2>/dev/null; do :; done
...