无法从临时文件中读取

Unable to read from temporary file

我正在尝试调用一个写入临时文件的外部进程,该文件是我通过 withSystemTempFile. After the process exits, I'm able to cat the content of that file, but the application itself fails with openFile: resource busy (file is locked) error when trying to read that file with readFile. I followed suggestions in answers to this 问题获得的,并使用了 readFile 的惰性版本。这是一个示例代码:

module Main where

import Prelude hiding (readFile)
import System.Process (createProcess, shell)
import System.IO (Handle, hShow)
import System.IO.Temp (withSystemTempFile)
import System.IO.Strict (readFile)

main :: IO ()
main = withSystemTempFile "temp.txt" doSomeStuff
    where
        doSomeStuff :: FilePath -> Handle -> IO ()
        doSomeStuff tempFilePath tempFilePathHandle = do
            putStrLn tempFilePath
            createProcess $ shell $ "echo \"test\" >> " ++ tempFilePath
            getLine -- It's here just to stop program while I check that I can actually "cat" the temp file
            -- here I'm able to cat the temp file
            contents <- readFile tempFilePath -- here the command fails with /tmp/temp23900-0.txt: openFile: resource busy (file is locked)
            putStrLn contents

我仍在思考 Haskell,所以我希望这不是显而易见的事情,因为我 运行 没有想法。给出了什么?

withSystemTempFile 打开它为您创建的临时文件,因此您无需再次打开它。 readFile 使用文件名而不是文件句柄,因此您知道它一定是试图自己打开它。 readFile 的等效项,但对于您已经打开的文件是 hGetContents,因此要解决此问题,请将 readFile tempFilePath 替换为 hGetContents tempFilePathHandle(并更新您的 import相应地)。

这个错误是因为withSystemTempFile在进入时锁定了文件。它在 return 中为您提供句柄(在您的代码中称为 tempFilePathHandle),因此您可以使用句柄读取文件,如下所示:

contents <- hGetContents tempFilePathHandle

编辑

这是因为 GHC 在内部实现了一个 readers-writer 锁来跟踪它打开了哪些文件,以及需要哪些权限。 readers-writer 锁只允许一个 writer(独占访问)或多个 readers(共享访问)。

在这种情况下,withSystemTempFile 获得临时文件的写入器锁,因此 readFile 无法获得它需要的 reader 锁(因为,同样,写入器lock 防止 GHC 获得对该文件的任何 reader 锁)。

这里是 link 实现锁的 C 代码。正如@luqui 在下面的评论中所建议的那样,它可能不是最佳解决方案,尤其是因为 GHC 不请求任何 OS 级别的锁,因此像 cat 这样的进程仍然可以访问和修改文件.这在惰性上下文中可能更有意义,在惰性上下文中读取 写入文件会产生难以预测的结果。