【发布时间】:2014-01-30 22:32:56
【问题描述】:
我有一个程序使用 C++ 从文件“foo”中读取:
pFile = fopen ("foo" , "r");
如果文件是命名管道,我希望它停止执行其余的函数。有没有办法在打开文件之前检查文件是否是命名管道?
我使用 python 发现了完全相同的问题: Check if file is a named pipe (fifo) in python?我可以在 C++ 中做类似的事情吗?
【问题讨论】:
我有一个程序使用 C++ 从文件“foo”中读取:
pFile = fopen ("foo" , "r");
如果文件是命名管道,我希望它停止执行其余的函数。有没有办法在打开文件之前检查文件是否是命名管道?
我使用 python 发现了完全相同的问题: Check if file is a named pipe (fifo) in python?我可以在 C++ 中做类似的事情吗?
【问题讨论】:
来自man 2 stat:
int fstat(int filedes, struct stat *buf);...定义了以下 POSIX 宏以使用 st_mode 字段检查文件类型:
S_ISFIFO(m) FIFO (named pipe)?
所以struct stat st; ... !fstat(fileno(pFile, &st) && S_ISFIFO(st.st_mode) 应该可以工作。
编辑:另见 SzG 的出色 answer,以及 Brian 对它的评论。
【讨论】:
S_ISFIFO(st.st_mode),这是一个测试管道或FIFO特殊文件的宏。目前尚不清楚您可以将管道与 FIFO 区分开来。
在fopen() 之后停止执行可能为时已晚。这是因为 open() 系统调用将阻塞,直到有人打开 FIFO 进行写入。相反,在fopen() 之前使用stat() 系统调用(在Unix/Linux 上)来找出答案。
【讨论】:
stat 然后fopen 会导致TOCTTOU 漏洞。如果这是一个安全问题,最好的解决方案是open non-blocking 然后fstat 和fdopen。
open() 意味着随后的 read() 调用是非阻塞的。但是查看手册页确认open() 本身也变得非阻塞。
在现代 C++ 中也有 filesystem library,它从 C++17 开始可用 #include <experimental/filesystem>,在 C++14 中它是 experimental #include <experimental/filesystem>
所以你现在可以使用is_fifo()。 filesystem::path 类可以从std::string 构造,它可以从const char* 构造,所以filesystem::is_fifo("/path/to/file") 将按预期工作。但是这个版本会抛出异常,所以bool is_fifo( const std::filesystem::path& p, std::error_code& ec ) noexcept;是你的选择。
#if __cplusplus >= 201703L
#include <filesystem>
namespace filesystem = std::filesystem;
#else
#include <experimental/filesystem>
namespace filesystem = std::experimental::filesystem;
#endif
bool is_fifo(const char *path)
{
std::error_code ec;
bool res = filesystem::is_fifo(path, ec);
if (ec.value() != 0)
std::cerr << ec.message() << std::endl;
return res;
}
别忘了这条通知:
9.1 之前的 GNU 实现需要与
-lstdc++fs链接,而 LLVM 9.0 之前的 LLVM 实现需要与-lc++fs链接。
【讨论】: