有没有办法在Haskell中重命名文件而不覆盖现有文件?

时间:2017-01-13 17:24:27

标签: haskell

我想在Haskell中重命名文件而不覆盖已存在的文件。如果目标文件存在,我想在我的代码中处理(通过在文件名中附加内容)。

来自System.Directory的{​​{3}}说明:

  

renameFile old new将现有文件系统对象的名称从old更改为new。如果new对象已存在,则它将被old对象原子替换。两条路径都不能引用现有目录。

是否有任何现有的模块或命令可以让我重命名而不会覆盖?

我知道我可以自己做检查。如果有经验的人写的功能,我会感觉很多。被覆盖的文件已经不复存在了。

更新

我想根据EXIF(类似于jhead)的创建数据或标准化为照片拍摄时区的文件系统时间戳重命名照片,视频,实时照片。可能是两张照片是在完全相同的时间拍摄并最终使用相同的名称:2017-01-12 – 11-12-11.jpg必须不会发生。第二张照片的名称应为2017-01-12 – 11-12-11a.jpg

2 个答案:

答案 0 :(得分:2)

这是一个潜在的解决方案:

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子句中的“&gt;&gt;”可能是一个问题。)

如评论中所述,文件系统中存在潜在的竞争条件,其中两个进程尝试同时创建或重命名具有相同名称的文件。但是,正如您所指出的,这对您来说不太可能是一个问题。

如果renameSafely返回IO False,则只需尝试其他名称。 : - )

答案 1 :(得分:2)

POSIX能够创建新文件:原子地检查文件是否存在,只有通过O_EXCL标志open()才能创建文件。这使您可以在更明显的实现中避免竞争条件,在这种实现中,两个进程可以在它们创建文件之前检查文件是否存在,从而导致一个进程覆盖另一个进程。这可以在这里有所帮助:想法是在目标上专门创建一个空文件,然后仅在独占创建成功时才用重命名覆盖它。如果独占创建失败,则另一个进程已创建该文件。这在Haskell的unix package中通过openFd 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}选项,可在O_EXCL的结果调用中设置open()标记。

Windows具有类似的功能,通过CREATE_NEW标记显示到CreateFile。还有一个MOVEFILE_REPLACE_EXISTING <form className="commentForm" onSubmit={this.handleSubmit}> <input type="text" placeholder="Your name" value={this.state.author} onChange={this.handleAuthorChange} /> <input type="text" placeholder="Say something..." value={this.state.text} onChange={this.handleTextChange} /> <input type="submit" value="Post" /> </form> 旗帜看起来可能有用,但我从来没有使用它,文档也不是100%清楚的。这些是在Haskell的MoveFileEx中公开的。

不幸的是,目前似乎并不是一种可移植的方法。