无法从临时文件中读取
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
这样的进程仍然可以访问和修改文件.这在惰性上下文中可能更有意义,在惰性上下文中读取 和 写入文件会产生难以预测的结果。
我正在尝试调用一个写入临时文件的外部进程,该文件是我通过 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
这样的进程仍然可以访问和修改文件.这在惰性上下文中可能更有意义,在惰性上下文中读取 和 写入文件会产生难以预测的结果。