savruk
savruk

Reputation: 545

Find a given string in given path

As title states, I am trying to find a given string within a given path. Here is what I come up so far:

getRecursiveContents :: FilePath -> IO [FilePath]
getRecursiveContents topdir = do
  names <- getDirectoryContents topdir
  let properNames = filter (`notElem` [".", ".."]) names
  paths <- forM properNames $ \name -> do
    let path = topdir </> name
    isDirectory <- doesDirectoryExist path
    if isDirectory
      then getRecursiveContents path
    else return [path]
  return (concat paths)

findInFile:: String -> FilePath -> IO Bool
findInFile needle filePath= do
  content <- readFile filePath
  return (needle `L.isInfixOf` content)

findInFolder:: (String -> Bool) -> FilePath -> String -> IO [IO Bool]
findInFolder p path needle = do
  files <- getRecursiveContents path
  return (map (findInFile needle) (filter p files))

find = findInFolder (\p -> takeExtension p `elem` [".py", ".xml", ".html"])

I can :

*Main> findInFile "search_string" "./path/to/a/file"
True

Which is perfect but I cannot do the same search for a folder:

*Main> find "./path/to/a/folder" "search_string"
*Main>

In my file system ./path/to/a/file is located under ./path/to/a/folder. Thus I was expecting the same result.

What am I doing wrong?

Note: getRecursiveContents is from real world haskell.

Upvotes: 2

Views: 58

Answers (1)

user2407038
user2407038

Reputation: 14598

It does indeed work. The only issue is with how things are printed. When you type some expressions into ghci, it will call print on that expression. If the value has type IO x, it will execute the IO action and print x only if it has a Show instance; otherwise it prints no additional information. find "./path/to/a/folder" "search_string" produces a list of IO actions, which have no Show instance. You can get the result of find, which is again a list of IO actions, and then execute them:

> x <- find "./path/to/a/folder" "search_string"
> sequence x
> [True, False ...

Likely you wanted to do this originally in your function. Simply make the following changes:

findInFolder:: (String -> Bool) -> FilePath -> String -> IO [Bool]
findInFolder p path needle = do
  files <- getRecursiveContents path
  mapM (findInFile needle) (filter p files)

Now findInFolder will work as you expect.

Upvotes: 3

Related Questions