CMC*_*kai 2 bash pipe io-redirection process-substitution
这是一个 bash 脚本示例,它将所有输出重定向到一个文件(并在屏幕上显示输出):
# writing to it
exec > >(tee --ignore-interrupts ./log)
exec 2>&1
echo "here is a message"
# reading from it again
cat ./log
Run Code Online (Sandbox Code Playgroud)
现在我不想创建文件./log
。我想将所有标准输出保留在内存中,并能够稍后在脚本中再次读取。此外,我处于可能没有安装根文件系统的情况。
我尝试使用进程替换而不是 来执行此操作./log
,但我似乎无法理解如何将进程替换创建的文件描述符传递给后续命令以读取我刚刚编写的内容。
这是另一个例子:
# can I make ./log a temporary file descriptor, an in-memory buffer?
exec 3>./log 4<./log
# writes
echo >&3 "hello"
# reads
cat <&4
Run Code Online (Sandbox Code Playgroud)
如果您想全局重定向碰巧编写的所有内容(就像您现在所做的那样),这很棘手,但可以一起破解。
我强烈建议,如果可能的话,只需通过普通管道来完成。只需将您所做的一切都包装在一个子外壳中。在这种情况下
(
echo "this is the message"
other stuff
) | cat
Run Code Online (Sandbox Code Playgroud)
或者只是使用“$()”语法将所有内容写入变量。
下一个方法是使用您所做的,但写入 tmpfs 或/dev/shm
它们是否可用。这很简单,但您必须知道基于 ram 的文件系统是什么(如果可能的话,设置它们)。
另一种方法是使用mkfifo
. 在这两种情况下,您都需要自己清理。
编辑:
我有一个非常丑陋的黑客,但我打赌有人可以改进它。
#!/bin/bash
exec 3>&1
exec > >( tee >( ( tac && echo _ ) | tac | (read && cat > ./log) ) )
echo "lol"
sleep 5
echo "lol"
echo "finished writing"
exec >&-
exec >&3
exec 3>&-
echo "stdout now reopen"
sleep 1 #wait if the file is still being written asynchronously
cat ./log
Run Code Online (Sandbox Code Playgroud)
它是如何工作的:首先,你有一个tee
这样你就可以看到发生了什么。这反过来输出到另一个进程替换。在那里,你有一个技巧tac|tac
(因为tac
需要整个输入才能开始输出)在继续之前等待整个流完成。最后一部分是在一个子shell中,它实际上将它输出到一个文件中。当然,最终的 shell 会在实例化后立即在文件系统中创建输出文件,如果那是唯一的一行的话。因此,必须首先完成等待输入最终到来的事情,以延迟文件创建。为此,我首先使用 echo 输出一个虚拟行,然后读取并丢弃它。read
直到您关闭文件描述符为止的块,向tac
是时候了。因此,最后关闭 stdout 文件描述符。我还在保存了原始文件stdout
在打开进程替换之前,为了在最后恢复它(再次使用cat
)。那里有一个sleep 5
,所以我可以检查ls
文件是否真的不是太早创建。最后的睡眠更棘手......子shell是异步的,如果有很多输出,你会tac
在文件真正存在之前等待两个s做他们的事情。所以合理地,你可能需要做一些其他的事情来检查事情是否真的完成了。例如,&& touch sentinel
在最后一个子shell 的末尾,然后while [ ! -f sentinel ]; do sleep 1; done && rm sentinel
在您最终使用该文件之前。
总而言之,两个进程替换以及在内部的另外两个子壳和 2 个管道。这是我写过的最丑陋的事情之一......但它应该只在您关闭标准输出时创建文件,这意味着它得到了很好的控制并且可以在您的文件系统准备好时完成。