How could i find path to file by his name? - haskell

Sooo guys I have to find path to the file by his name
I've found this func. findExecutable but it's not working
F.e:
fileName <- getLine ("file.txt")
filePath <- findExecutable fileName
case filePath of
Nothing -> error "I can't find this file "
Just path -> print path
Even when I input "/home/.../file.txt" It's not working
How can I fix it
The type of func is findExecutable :: String -> IO (Maybe FilePath)
I give to this func "String" I'm check the result with const. case of but all time i'm getting error

This would happen if file.txt is not, in fact, executable.
The documentation for findExecutable specifically states (bold emphasis mine):
On non-Windows platforms, the behavior is equivalent to findFileWith using the search directories from the PATH environment variable and testing each file for executable permissions.
Try giving it the executable permission:
chmod +x /home/.../file.txt
Then try findExecutable again. Should work.
If you're trying to find the file regardless of it being executable or not, take a look at findFile instead.
Keep in mind, however, that, while for executables there is a rough definition of "find" (meaning "find anywhere on PATH"), this concept is generally not defined for regular files. This means that if you want to "find" a file, you have to specify precisely where you'd like to find it. This is what findFile's first parameter is for.

Related

Running an Action if part of a file changes

What is the recommended way of running some Action if part of a file changes?
My use-case is given a file that I know exists (concretely elm-package.json), run a shell command (elm package install --yes) if part of the file changes (the dependencies field).
It seems that the Oracle abstraction exposes comparing a value to the last (via Eq). So I tried a newtype like:
newtype ElmDependencies = ElmDependencies () deriving ...
type instance RuleResult ElmDependencies = String
But now, I get stuck actually using this function of type ElmDependencies -> Action String, since the rule I want to write doesn't actually care what the returned String is, it simply wants to be called if the String changes.
In other words,
action $ do
_ <- askOracle (ElmDependencies ())
cmd_ "elm package install --yes"
at the top-level doesn't work; it will run the action every time.
Your askOracle approach is pretty close, but Shake needs to be able to
identify the "output" of the action, so it can give it a persistent name
between runs, so other steps can depend on it, and use that persistent name to avoid recomputing. One way to do that is to make the action create a stamp file, e.g.:
"packages.stamp" *> \out -> do
_ <- askOracle $ ElmDependencies ()
cmd_ "elm package install --yes"
writeFile' out ""
want ["packages.stamp"]
Separately, an alternative to using Oracle is to have a file
elm-package-dependencies.json which you generate from
elm-package.json, write using writeFileIfChanged (which gives you Eq for files), and depend on that
file in packages.stamp. That way you get Eq on files, and can also
easily debug it or delete the -dependencies.json file to force a rerun.

Finding all the files under a directory in turtle that match a pattern

I'd like to use the find function from the turtle package such that it matches any file path (in order to get an equivalent behavior to find . in bash). However I cannot find a wildcard pattern that I can use with this function.
find :: Pattern a -> FilePath -> Shell FilePath
I guess I could construct a pattern that matches any character zero or more times, but I'd like to avoid re-inventing the wheel.
lsif sounds more like what you want. The documentation contains an example how to print the complete tree:
lstree = lsif (\_ -> return True)
So in your case, you would use
lstree "."
Note that the output between find and lstree "." differs slightly: the original path isn't duplicated in the latter.

How can I ensure the destination file does not exist when renaming a file with Haskell, to avoid overwriting it?

I want to rename a file in Haskell without overwriting an already existing one. In case the target file exists I want to deal with that in my code (by appending something to the file name).
The description of renameFile from System.Directory says:
renameFile old new changes the name of an existing file system object from old to new. If the new object already exists, it is atomically replaced by the old object. Neither path may refer to an existing directory.
Is there any existing module or command that would let me rename without overwriting?
I know I can do the checks myself. I'd just feel much better if there was a function written by someone experienced. Overwritten files are gone for good.
Update
I want to rename photos, videos, live photos by creation data from either EXIF (similar to jhead) or the file system timestamp normalized to the timezone the photo was taken in. It might be that two photos were taken at exactly the same time and would end up with the same name: 2017-01-12 – 11-12-11.jpg. This must not happen. The second photo should be called something like 2017-01-12 – 11-12-11a.jpg.
POSIX has the ability to create a new file: atomically check a file exists and only create it if it does not, via the O_EXCL flag to open(). This lets you avoid the race condition in the more obvious implementation in which two processes may check that a file doesn't exist before either of them creates it, causing one process to overwrite the other's file. This can help here: the idea is to exclusively create an empty file at the target, and then overwrite it with a rename only if the exclusive creation succeeded. If the exclusive creation failed then another process already created the file. This is exposed in Haskell's unix package, via the openFd function, which either succeeds or else throws an IOException. It can be used like this:
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
The key part is the {exclusive = True} option, which sets the O_EXCL flag on the resulting call to open().
Windows has a similar ability, exposed via the CREATE_NEW flag to CreateFile. There's also a MOVEFILE_REPLACE_EXISTING flag to MoveFileEx that looks like it might be useful, but I've never used it and the documentation is not 100% clear to me. These are exposed in Haskell's Win32 package.
Unfortunately there doesn't currently seem to be a portable way of doing this.
Here is one potential solution:
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
(Disclaimer: I didn't run this through GHC to ensure that it compiles; the ">>" in the then clause might be an issue.)
As noted in the comments, there is a potential race condition in the file system with two processes trying to create or rename a file with the same name at the same time. However, as you pointed out, that is unlikely to be an issue for you.
If renameSafely returns IO False, then simply try another name. :-)

avoid recursion into specifc folder using filemanip

I am exploring the filemanip library to search for markdown files in a path including subfolders
import System.FilePath.Find
find always (fileType ==? RegularFile &&? extension ==? ".md") "a/path/"
is there any way to specify a folder name or pattern into which it should not recurse
Looking at the documentation we can see that find takes as first argument a RecursionPredicate which in turn is just FindClause Bool.
Based on this we can see that we have to pass in a custom RecursionPredicate to find other than always.
One example is to ignore .git directories:
notGit :: FindClause Bool -- or via type alias also a RecursionPredicate
notGit = directory /=? ".git"
We then just use our new recursion predicate with find:
find notGit (fileType ==? RegularFile &&? extension ==? ".md") path
Note also the special combinators for predicates to e.g. compose a notSvn predicate with our notGit predicate via (||?) to get a predicate that enters neither directories.

Aliases in Haskell/GHCI

Is it possible to set aliases in the ghci.conf file?
For example I have alias sbh='cd Desktop/Sandbox/Haskell' in bash.bashrc which lets me quickly jump to the specified folder. Is the same thing possible in ghci by putting an alias in the ghci.conf file?
I already have a few commands in ghci.conf but I would like to have multiple aliases set up to jump to folder locations without having to use :cd home/sandbox/foo/bar all of the time. I cant find anything on google so either its never been considered before or am just missing something very simple.
The :def command can do this:
:def sbh const $ return ":cd Desktop/Sandbox/Haskell"
As you can see it is a little more complicated than just giving a substitution string: It takes a Haskell function of type String -> IO String which the newly defined command applies to its argument string to calculate new commands to run.
Then in GHCI :sbh to invoke.
GHCI macros should give you what you're looking for. See: https://www.haskell.org/ghc/docs/7.6.2/html/users_guide/ghci-commands.html as a reference.
Search for "macros" (or :def, which is the command to define macros). You can put these in the ghci.conf file.
For example (from the same URL indicated above):
Prelude> let mycd d = Directory.setCurrentDirectory d >> return ""
Prelude> :def mycd mycd
Prelude> :mycd ..
I hope this helps.
Possible not exactly what you need, but in case the quick-jumping function suffices try this as a first fix (invoked by :sbh):
:def sbh (\arg -> return ("System.Directory.setCurrentDirectory \"Desktop/Sandbox/Haskell\""))
Your later solution might make use of the arg reference like in:
:def sbh (\arg -> return ("System.Directory.setCurrentDirectory " ++ "\"" ++ args ++ "\""))
Invoke the latter which by :sbh Desktop/Sandbox/Haskell then.

Resources