Golang 虚拟文件
Golang Virtual File
我有一个封闭的 source 应用程序,它将一个文件作为输入,计算它的哈希值并做一些我无法控制的其他事情。修改源代码或逆向工程是不可行的。
该程序旨在处理常规文件,但我需要从 HDFS 提供一个非常大的文件。复制文件将花费太多时间,并且 space 占用磁盘空间。所以一直在想用FUSE,但是没找到好的解决办法。我尝试使用命名管道如下:
func readFile(namenode, path string, pipe *os.File) {
client, err := hdfs.New(namenode)
log.Println(err, client)
hdfsFile, err := client.Open(path)
if err != nil {
log.Fatal(err)
}
log.Println(hdfsFile)
// written, err := io.Copy(pipe, hdfsFile)
bytes := make([]byte, 4096)
for {
read, err := hdfsFile.Read(bytes)
log.Println(read, err)
if err != nil {
break
}
written, err := pipe.Write(bytes)
log.Println(written, err)
}
err = pipe.Close()
log.Println(err)
}
我知道上面的代码不完整,测试文件是 10MB,但是在读取 8 次后,4096 字节的命名管道缓冲区变满,另一个程序将其全部占用并关闭管道。
但是过了一会儿,另一个正在读取管道的程序关闭了管道,我得到了管道损坏的错误。是否可以创建除 fuse 和 pipe 之外的虚拟文件?
我认为您对 FUSE 的想法是正确的。如果没有上游应用程序的源代码,很难说出它试图使用什么文件语义(尽管使用 strace 的一些时间可能有助于阐明正在发生的事情。也许......)。
无论如何,我都会看一下 the Go-FUSE project, specifically the hello.go example,它准确地展示了如何很好地处理单个文件的情况。
我明白这个问题是闭源程序 2 需要一个文件名并且不接受直接来自 stdin?
的输入
当 运行 程序连接 stdin 和 stdout 时,您可以使用标准 Unix 风格的管道一起处理。命名管道可能会有问题,为此使用 FUSE 过于复杂。
您可以让您的程序 1 输出到 stdout。并为闭源程序 2 提供虚拟文件名 /dev/stdin
,如下所示:
program1 | program2 /dev/stdin
这是假设您正在研究 Linux(您没有指定但我假设是因为您在谈论 FUSE)。
如果 program2 关心文件名(例如需要特定的扩展名),您可以通过创建符号 link 并将所需名称指向 /dev/stdin
来解决这个问题并提供符号 link 的名称作为 program2:
的参数
ln -s /dev/stdin file.ext
program1 | program2 file.ext
rm -f file.ext
None 如果 program2 需要一个它可以 stat 的真实文件,那么它的
None 将起作用,但在这种情况下,这应该不是问题(因为从program2 接受命名管道的问题)。
此外,如果程序 2 需要来自 stdin 的键盘输入,这种方法将不起作用。
我有一个封闭的 source 应用程序,它将一个文件作为输入,计算它的哈希值并做一些我无法控制的其他事情。修改源代码或逆向工程是不可行的。
该程序旨在处理常规文件,但我需要从 HDFS 提供一个非常大的文件。复制文件将花费太多时间,并且 space 占用磁盘空间。所以一直在想用FUSE,但是没找到好的解决办法。我尝试使用命名管道如下:
func readFile(namenode, path string, pipe *os.File) {
client, err := hdfs.New(namenode)
log.Println(err, client)
hdfsFile, err := client.Open(path)
if err != nil {
log.Fatal(err)
}
log.Println(hdfsFile)
// written, err := io.Copy(pipe, hdfsFile)
bytes := make([]byte, 4096)
for {
read, err := hdfsFile.Read(bytes)
log.Println(read, err)
if err != nil {
break
}
written, err := pipe.Write(bytes)
log.Println(written, err)
}
err = pipe.Close()
log.Println(err)
}
我知道上面的代码不完整,测试文件是 10MB,但是在读取 8 次后,4096 字节的命名管道缓冲区变满,另一个程序将其全部占用并关闭管道。
但是过了一会儿,另一个正在读取管道的程序关闭了管道,我得到了管道损坏的错误。是否可以创建除 fuse 和 pipe 之外的虚拟文件?
我认为您对 FUSE 的想法是正确的。如果没有上游应用程序的源代码,很难说出它试图使用什么文件语义(尽管使用 strace 的一些时间可能有助于阐明正在发生的事情。也许......)。
无论如何,我都会看一下 the Go-FUSE project, specifically the hello.go example,它准确地展示了如何很好地处理单个文件的情况。
我明白这个问题是闭源程序 2 需要一个文件名并且不接受直接来自 stdin?
的输入当 运行 程序连接 stdin 和 stdout 时,您可以使用标准 Unix 风格的管道一起处理。命名管道可能会有问题,为此使用 FUSE 过于复杂。
您可以让您的程序 1 输出到 stdout。并为闭源程序 2 提供虚拟文件名 /dev/stdin
,如下所示:
program1 | program2 /dev/stdin
这是假设您正在研究 Linux(您没有指定但我假设是因为您在谈论 FUSE)。
如果 program2 关心文件名(例如需要特定的扩展名),您可以通过创建符号 link 并将所需名称指向 /dev/stdin
来解决这个问题并提供符号 link 的名称作为 program2:
ln -s /dev/stdin file.ext
program1 | program2 file.ext
rm -f file.ext
None 如果 program2 需要一个它可以 stat 的真实文件,那么它的
None 将起作用,但在这种情况下,这应该不是问题(因为从program2 接受命名管道的问题)。
此外,如果程序 2 需要来自 stdin 的键盘输入,这种方法将不起作用。