2017-01-13 4 views
2

Я хочу переименовать файл в Haskell без перезаписи уже существующего. Если целевой файл существует, я хочу иметь дело с этим в своем коде (добавив что-то к имени файла).Есть ли способ переименовать файлы в Haskell без перезаписывания существующих?

Описание renameFile из System.Directory говорит:

renameFile old new изменяет имя существующего объекта файловой системы от old к new. Если объект new уже существует, он атомарно заменяется объектом old. Ни один из путей не может ссылаться на существующий каталог.

Есть ли какой-либо существующий модуль или команда, которая позволила бы мне переименовать без перезаписи?

Я знаю, что могу делать чеки самостоятельно. Я бы просто почувствовал больше лучше, если бы была функция, написанная кем-то испытанным. Записанные файлы ушли навсегда.

Update

Я хочу, чтобы переименовать фотографии, видео, живые фотографии от данных для создания либо из EXIF ​​(по аналогии с jhead) или файловой системы метка времени нормализуется к часовому поясу была сделана фотография. Это может быть что две фотографии были сделаны точно в одно и то же время и в конечном итоге получили бы то же имя: 2017-01-12 – 11-12-11.jpg. Это не должно быть. Вторую фотографию следует называть чем-то вроде 2017-01-12 – 11-12-11a.jpg.

+1

Проблема с проверкой заключается в том, что между проверкой и фактическим переименованием какой-либо другой процесс мог создать целевой файл. Это классическое состояние гонки. Я не думаю, что это исправно. – melpomene

+0

Как другие программы занимаются этим? 'jhead' переименовывает файлы изображений и добавляет буквы в имя файла, чтобы предотвратить перезапись существующих файлов. Или вы считаете, что у них одна и та же проблема, и просто подумайте, что маловероятно, что новый файл будет создан или другой будет переименован во время проверки и переименования? – Dominik

+2

О, я тупой. Это можно сделать безопасно, используя 'link' /' unlink' вместо 'rename'. 'link (oldname, newname)' будет терпеть неудачу, если 'newname' уже существует. В Haskell это будут ['createLink' и' removeLink'] (http://hackage.haskell.org/package/unix-2.7.2.1/docs/System-Posix-Files.html#g:8) соответственно , – melpomene

ответ

1

Вот один из возможных решений:

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 может быть проблемой)

Как отмечено в комментариях, в файловой системе существует потенциальное состояние гонки с двумя процессами, которые пытаются создать или переименовать файл с тем же именем одновременно. Однако, как вы указали, это вряд ли будет проблемой для вас.

Если renameSafely возвращает IO False, тогда просто попробуйте другое название. :-)

+0

Итак, если бы я хотел добавить что-то к имени файла, я бы удалял возвращаемое значение Boolean и либо рекурсивно вызывал функцию, либо проверял разные пути '2017-01-11 - 11-21-21a.jpg',' 2017- 01-11 - 11-21-21b.jpg', ... и переименуйте тогда. Korrekt? Или вы сделаете это из другой функции? – Dominik

+0

@ Dominik Оба подхода кажутся мне удобными. Вы также можете написать 'genFreshName :: String -> IO String', который возвращает неиспользуемое имя файла, возможно добавив трейлер, и после использования обычного переименования. (Та же гонка все еще здесь, очевидно) – chi

+0

Если я перебираю список путей в Haskell, в моем скрипте не будет никаких условий гонки, правильно? Поэтому Haskell не будет пытаться распараллелить работу и переименовать в разные процессы. Единственное условие гонки здесь - из внешнего мира. Некоторая другая программа может работать с файлами одновременно. – Dominik

Смежные вопросы