Невозможно прочитать из временного файла - PullRequest
3 голосов
/ 12 апреля 2020

Я пытаюсь вызвать внешний процесс, который записывает во временный файл, который я получаю с помощью с помощью SystemTempFile . После завершения процесса я могу cat содержимое этого файла, но само приложение завершается с ошибкой openFile: resource busy (file is locked) при попытке прочитать этот файл с readFile . Я следовал предложениям в ответах на этот вопрос и использовал ленивую версию 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, поэтому я надеюсь, что это не что-то очевидное, так как у меня закончились идеи. Что дает?

Ответы [ 2 ]

4 голосов
/ 12 апреля 2020

withSystemTempFile открывает временный файл, который он создает для вас, поэтому вам не нужно открывать его снова. readFile принимает имя файла, а не его дескриптор, так что вы знаете, что он пытается открыть его сам. Эквивалент readFile, но для файла, который у вас уже открыт, - hGetContents, поэтому, чтобы устранить проблему, замените readFile tempFilePath на hGetContents tempFilePathHandle (и обновите import соответственно).

3 голосов
/ 12 апреля 2020

Эта ошибка вызвана тем, что withSystemTempFile блокирует файл при входе. Он возвращает вам свой дескриптор (в вашем коде он называется tempFilePathHandle), поэтому вы можете прочитать файл с помощью дескриптора, например:

contents <- hGetContents tempFilePathHandle

EDIT

Это потому, что GH C внутренне реализует блокировку reader-writer , чтобы отслеживать, какие файлы он открыл и какие разрешения требуются. Блокировка чтения-записи разрешает либо только одного автора (эксклюзивный доступ), либо несколько читателей (общий доступ).

В этом случае withSystemTempFile получает блокировку записи для временного файла, и поэтому readFile не может получить блокировку считывателя, в которой он нуждается (потому что, опять же, блокировка записи запрещает GH C от получить любой читатель блокирует этот файл).

Вот ссылка на код C, реализующий блокировку. Как подсказывает @luqui в комментарии ниже, это, возможно, не оптимальное решение, тем более что GH C не запрашивает каких-либо блокировок на уровне ОС, поэтому такие процессы, как cat, могут по-прежнему обращаться к файлам и изменять их. Это может иметь больше смысла в ленивом контексте, где чтение и запись в файл приведет к трудно предсказуемым результатам.

...