首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >如何确保用Haskell重命名文件时目标文件不存在,以避免覆盖它?

如何确保用Haskell重命名文件时目标文件不存在,以避免覆盖它?
EN

Stack Overflow用户
提问于 2017-01-13 17:24:27
回答 2查看 806关注 0票数 2

我想在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

EN

回答 2

Stack Overflow用户

发布于 2018-05-30 04:32:36

POSIX能够创建一个新的文件:原子性地检查一个文件是否存在,并且只有当它不存在时才创建它,通过O_EXCL标志到open()。这使您可以在更明显的实现中避免争用条件,在此实现中,两个进程可能在创建文件之前检查文件是否不存在,从而导致一个进程覆盖另一个进程的文件。这在这里会有所帮助:这个想法是在目标上独占地创建一个空文件,然后只有在独占创建成功的情况下,才用重命名覆盖它。如果独占创建失败,则另一个进程已经创建了该文件。这在Haskell的 package中通过 function公开,后者要么成功,要么抛出一个IOException。它可以这样使用:

代码语言:javascript
运行
复制
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中。

不幸的是,目前似乎还没有一种可移植的方法来做到这一点。

票数 3
EN

Stack Overflow用户

发布于 2017-01-14 00:08:52

以下是一个潜在的解决方案:

代码语言:javascript
运行
复制
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,那么只需尝试另一个名称。:-)

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/41640151

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档