在c++中可以输出到cout
和cerr
,分别由文件描述符1
和2
处理。在c++程序之外,我可以将输出重定向到我想要的任何地方(在本例中,将其写入两个单独的文件):
$ my-program 1>output 2>errors;
我是否只能使用文件描述符1
和2
,或者我可以"创建我自己的"?比方说,我想要第三个输出保存调试信息,或者第四个输出给管理员发邮件?
$ my-program 1>output 2>errors 3>>/logs/my-program.log 4>&1 | scripts/email-admin.sh;
我可以在我的程序中写入文件描述符的3和4吗?
在包装器脚本中打开所有文件通常不是一个好的设计。当你想让你的程序更智能,能够关闭一个大的日志文件并启动一个新的,你需要在你的程序中的逻辑。
但是要回答实际的问题:
是,您可以让shell打开您喜欢的任何编号的文件描述符,用于输入、输出或两者。让父进程在execve(2)
之前打开它们与在子进程中使用代码打开它们所得到的结果相同(至少在POSIX系统上,stdin/out/err就像其他文件描述符一样,并不特殊)。文件描述符可以标记为close-on-exec或不标记为close-on-exec。使用open(2)
和O_CLOEXEC
,或者打开后使用fcntl(2)
设置FD_CLOEXEC
它们也不需要引用常规文件。它们中的任何一个都可以是tty、管道、块或字符设备文件、套接字甚至目录。(打开目录没有shell重定向语法,因为您只能在它们上使用readdir(3)
,而不能使用read(2)
或write(2)
。)
请参阅bash重定向教程。再举一个我自己的例子:
peter@tesla:~$ yes | sleep 60 4> >(cat) 5</etc/passwd 9>/dev/tcp/localhost/22 42<>/tmp/insecure_temp &
[1] 25644
peter@tesla:~$ ll /proc/$!/fd
total 0
lr-x------ 1 peter peter 64 Sep 9 21:31 0 -> pipe:[46370587]
lrwx------ 1 peter peter 64 Sep 9 21:31 1 -> /dev/pts/19
lrwx------ 1 peter peter 64 Sep 9 21:31 2 -> /dev/pts/19
l-wx------ 1 peter peter 64 Sep 9 21:31 4 -> pipe:[46372222]
lrwx------ 1 peter peter 64 Sep 9 21:31 42 -> /tmp/insecure_temp
lr-x------ 1 peter peter 64 Sep 9 21:31 5 -> /etc/passwd
l-wx------ 1 peter peter 64 Sep 9 21:31 63 -> pipe:[46372222]
lrwx------ 1 peter peter 64 Sep 9 21:31 9 -> socket:[46372228]
# note the rwx permissions: some are read-write, some are one-way
>(cmd)
进程替换语法扩展为像/dev/fd/63
这样的文件名。使用4> >(cmd)
将fd打开为fd 4。
将stderr重定向到管道需要对文件描述符进行一些调整,因为没有2| cmd
语法。2> >(cmd)
工作,但cmd
在后台运行:
peter@tesla:~$ (echo foo >&2 ) 2> >(wc) # next prompt printed before wc output
peter@tesla:~$ 1 1 4
peter@tesla:~$ ( echo foo >&2; ) 2>&1 | wc
1 1 4
处理将调试信息发送到其他地方的通常方法是选择一个直接写入文件的日志系统(而不是将其发送到另一个虚拟文件描述符,然后在bash中将该文件描述符重定向到file)。
一种选择是使用mkfifo,并让您的程序作为文件写入fifo,然后使用其他方法将fifo定向到其他位置。
邮件脚本的另一种选择是将邮件脚本作为c++程序中的子进程运行,并使用内部管道写入其标准输入。在Unix系统上,管道进入sendmail
是程序发送邮件的传统方式。
最好的办法是找到处理你想做的事情的库。
例子mkfifo
http://linux.die.net/man/3/mkfifo您使用mkfifo命令创建一个特殊的文件,然后您可以像往常一样将fopen
和fprint*
添加到它。您可以像传递其他文件名一样将文件名传递给程序。
bash部分看起来像这样:
mkfifo mailfifo
yourprogram mailfifo &
cat mailfifo | scripts/email-admin.s
管道到子进程
http://www.gnu.org/software/libc/manual/html_node/Pipe-to-a-Subprocess.html