在BASH脚本中间切换输出文件重定向

在BASH脚本中间切换输出文件重定向,bash,file-descriptor,tee,Bash,File Descriptor,Tee,我只想将脚本的输出保存在脚本开头不知道的地方。我尝试了一些东西,但我很确定它很难看。 有没有一种优雅的方法可以做到这一点: #!/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&g

我只想将脚本的输出保存在脚本开头不知道的地方。我尝试了一些东西,但我很确定它很难看。 有没有一种优雅的方法可以做到这一点:

#!/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
#/bin/bash
#这里我不知道在哪里写输出文件,所以我创建了一个tmpfile
fic=$(mktemp-t)
陷阱“rm-f$fic”出口
rm-f$fic
:>$fic
执行官3$fic
执行1>&3
执行2>&3
#一些代码特别是读取选项和dest dir
埃科富
dir=“”
#在这里,我终于知道我可以在哪里写我的输出
fic2=$dir/log.log
cp-f$fic$fic2
执行3>和-#关闭fd#3
执行1>>$fic2
执行2>&1
回音条
此外,我想对整个输出进行T形处理,比如$exec…>(tee$fic)$但我没有找到解决方案

非常感谢你的建议。
PJLM

如果您知道两个输出文件都在同一个文件系统上,您可以只
mv
输出文件。您打开的文件描述符将继续工作

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

如果您想
tee
输出,并且两个输出文件都在同一个文件系统上,您可以做几乎相同的事情(一旦
tee
打开文件进行写入,即使文件移动,它也会继续写入相同的fd)

请注意,我没有执行
>(tee“$log1”)
而是首先在shell中打开fd3,然后使用
>(tee/dev/fd/3)
。这是因为,否则存在一个潜在的争用条件,即当我们到达
mv
步骤时,
tee
不会打开文件。(
exec
仅等待运行
tee
的子shell启动,但是
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

exec>>(tee“$fic”)2>&1
?当然,简单地说,您所建议的@melpomene效果很好。但是我想改变脚本中间的重定向。所以你的解决方案在第一部分还可以,但在第二部分就不行了。好的,非常有用,它简化了我的脚本。现在,我只想在其中添加一些
tee
,以获得标准输出。编辑答案以添加
tee
变体。非常感谢。最后一句话对我来说是个好办法。非常有启发性。如果两个位置在同一个文件系统上,它将继续工作。如果它们在不同的分区上,那么您的旧句柄将写入一个未链接的副本。@CharlesDuffy我在答案的第三部分中谈到了这一点,但为了清楚起见,我将在顶部添加一些内容
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