我想在Haskell中重命名一个文件,而不覆盖一个已经存在的文件。如果目标文件存在,我想在我的代码中处理这个文件(通过在文件名后面添加一些东西)。
来自System.Directory
的对System.Directory
的描述是:
renameFile old new
将现有文件系统对象的名称从old
更改为new
。如果new
对象已经存在,则将其原子地替换为old
对象。两个路径都不能引用现有目录。
是否有任何现有的模块或命令允许我在不覆盖的情况下重命名?
我知道我可以自己做检查。如果有一个有经验的人写的函数,我会感觉好多了。被覆盖的文件永远消失了。
更新
我想重命名照片,视频,现场照片,通过创建数据从EXIF (类似于jhead
)或文件系统时间戳归一化的时区拍摄的照片。这可能是因为两张照片是在同一时间拍摄的,最终会有相同的名字:2017-01-12 – 11-12-11.jpg
。这种情况决不能发生。第二张照片应该叫做2017-01-12 – 11-12-11a.jpg
。
发布于 2018-05-30 04:32:36
POSIX能够创建一个新的文件:原子性地检查一个文件是否存在,并且只有当它不存在时才创建它,通过O_EXCL
标志到open()
。这使您可以在更明显的实现中避免争用条件,在此实现中,两个进程可能在创建文件之前检查文件是否不存在,从而导致一个进程覆盖另一个进程的文件。这在这里会有所帮助:这个想法是在目标上独占地创建一个空文件,然后只有在独占创建成功的情况下,才用重命名覆盖它。如果独占创建失败,则另一个进程已经创建了该文件。这在Haskell的 package中通过 function公开,后者要么成功,要么抛出一个IOException
。它可以这样使用:
module RenameNoOverwrite where
import Control.Exception
import Control.Monad
import Data.Bits
import System.Directory
import System.Posix.Files
import System.Posix.IO
renameFileNoOverwrite :: FilePath -> FilePath -> IO Bool
renameFileNoOverwrite old new = do
created <- handle handleIOException $ bracket createNewFile closeFd $ pure $ pure True
when created $ renameFile old new
return created
where
createNewFile = openFd new WriteOnly (Just defaultMode) defaultFileFlags {exclusive = True}
defaultMode = ownerReadMode .|. ownerWriteMode .|. groupReadMode .|. otherReadMode
handleIOException :: IOException -> IO Bool
handleIOException _ = return False
关键部分是{exclusive = True}
选项,它在对open()
的结果调用中设置O_EXCL
标志。
Windows具有类似的功能,通过CREATE_NEW
标志将其公开给CreateFile
。还有一个MOVEFILE_REPLACE_EXISTING
标志给MoveFileEx
,它看起来可能很有用,但我从未使用过它,而且文档对我来说也不是100%清楚。这些都暴露在Haskell的 package中。
不幸的是,目前似乎还没有一种可移植的方法来做到这一点。
发布于 2017-01-14 00:08:52
以下是一个潜在的解决方案:
import System.Directory (doesFileExist, renameFile)
-- | Rename a src file as tgt file, safely. If the tgt file exists, don't
-- rename and return False. Otherwise, rename src to tgt and return True.
renameSafely :: FilePath -> FilePath -> IO Bool
renameSafely src tgt = do
exists <- doesFileExist tgt
if not exists
then (renameFile src tgt >> return True)
else return False
(免责声明:我没有通过GHC运行此操作以确保其编译;then
子句中的“then
”可能是一个问题。)
正如注释中所指出的,文件系统中存在一个潜在的争用条件,两个进程试图同时创建或重命名同名的文件。然而,正如你所指出的,这对你来说不太可能是个问题。
如果renameSafely
返回IO False
,那么只需尝试另一个名称。:-)
https://stackoverflow.com/questions/41640151
复制相似问题