Add Glob module, update docs.
--HG-- extra : convert_revision : e114cb9154838d7f74c72671db22dbddb97e3a58
This commit is contained in:
parent
cb63c4f2a5
commit
097afc9831
5 changed files with 110 additions and 3 deletions
|
@ -13,6 +13,7 @@ Build-Depends: base, filepath, mtl, unix
|
|||
GHC-Options: -Wall -O2
|
||||
Exposed-Modules:
|
||||
System.FilePath.Find,
|
||||
System.FilePath.Glob,
|
||||
System.FilePath.GlobPattern,
|
||||
System.FilePath.Manip
|
||||
Extra-Source-Files: README
|
||||
|
|
22
README
22
README
|
@ -4,6 +4,28 @@ FileManip: expressive file manipulation
|
|||
This package provides functions and combinators for searching,
|
||||
matching, and manipulating files.
|
||||
|
||||
It provides four modules.
|
||||
|
||||
System.FilePath.Find lets you search a filesystem hierarchy efficiently:
|
||||
|
||||
find always (extension ==? ".pl") >>= mapM_ remove
|
||||
|
||||
System.FilePath.GlobPattern lets you perform glob-style pattern
|
||||
matching, without going through a regexp engine:
|
||||
|
||||
"foo.c" ~~ "*.c" ==> True
|
||||
|
||||
System.FilePath.Glob lets you do simple glob-style file name searches:
|
||||
|
||||
namesMatching "*/*.c" ==> ["foo/bar.c"]
|
||||
|
||||
System.FilePath.Manip lets you rename files procedurally, edit files
|
||||
in place, or save old copies as backups:
|
||||
|
||||
modifyWithBackup (<.> "bak")
|
||||
(unlines . map (takeWhile (/= ',')) . lines)
|
||||
"myPoorFile.csv"
|
||||
|
||||
|
||||
To build and install:
|
||||
|
||||
|
|
72
System/FilePath/Glob.hs
Normal file
72
System/FilePath/Glob.hs
Normal file
|
@ -0,0 +1,72 @@
|
|||
-- |
|
||||
-- Module: System.FilePath.Glob
|
||||
-- Copyright: Bryan O'Sullivan
|
||||
-- License: LGPL
|
||||
-- Maintainer: Bryan O'Sullivan <bos@serpentine.com>
|
||||
-- Stability: unstable
|
||||
-- Portability: everywhere
|
||||
|
||||
module System.FilePath.Glob (
|
||||
namesMatching
|
||||
) where
|
||||
|
||||
import Control.Exception (handle)
|
||||
import Control.Monad (forM)
|
||||
import System.FilePath.GlobPattern ((~~))
|
||||
import System.Directory (doesDirectoryExist, doesFileExist,
|
||||
getCurrentDirectory, getDirectoryContents)
|
||||
import System.FilePath (dropTrailingPathSeparator, splitFileName, (</>))
|
||||
import System.IO.Unsafe (unsafeInterleaveIO)
|
||||
|
||||
-- | Return a list of names matching a glob pattern. The list is
|
||||
-- generated lazily.
|
||||
namesMatching :: String -> IO [FilePath]
|
||||
namesMatching pat
|
||||
| not (isPattern pat) = do
|
||||
exists <- doesNameExist pat
|
||||
return (if exists then [pat] else [])
|
||||
| otherwise = do
|
||||
case splitFileName pat of
|
||||
("", baseName) -> do
|
||||
curDir <- getCurrentDirectory
|
||||
listMatches curDir baseName
|
||||
(dirName, baseName) -> do
|
||||
dirs <- if isPattern dirName
|
||||
then namesMatching (dropTrailingPathSeparator dirName)
|
||||
else return [dirName]
|
||||
let listDir = if isPattern baseName
|
||||
then listMatches
|
||||
else listPlain
|
||||
pathNames <- forM dirs $ \dir -> do
|
||||
baseNames <- listDir dir baseName
|
||||
return (map (dir </>) baseNames)
|
||||
return (concat pathNames)
|
||||
where isPattern = any (`elem` "[*?")
|
||||
|
||||
listMatches :: FilePath -> String -> IO [String]
|
||||
listMatches dirName pat = do
|
||||
dirName' <- if null dirName
|
||||
then getCurrentDirectory
|
||||
else return dirName
|
||||
names <- unsafeInterleaveIO (handle (const (return [])) $
|
||||
getDirectoryContents dirName')
|
||||
let names' = if isHidden pat
|
||||
then filter isHidden names
|
||||
else filter (not . isHidden) names
|
||||
return (filter (~~ pat) names')
|
||||
where isHidden ('.':_) = True
|
||||
isHidden _ = False
|
||||
|
||||
listPlain :: FilePath -> String -> IO [String]
|
||||
listPlain dirName baseName = do
|
||||
exists <- if null baseName
|
||||
then doesDirectoryExist dirName
|
||||
else doesNameExist (dirName </> baseName)
|
||||
return (if exists then [baseName] else [])
|
||||
|
||||
doesNameExist :: FilePath -> IO Bool
|
||||
doesNameExist name = do
|
||||
fileExists <- doesFileExist name
|
||||
if fileExists
|
||||
then return True
|
||||
else doesDirectoryExist name
|
|
@ -32,12 +32,12 @@ import System.FilePath (pathSeparator)
|
|||
--
|
||||
-- * @[!/range/]@ matches any character /not/ in /range/.
|
||||
--
|
||||
-- There are three extensions to the traditional glob syntax, taken
|
||||
-- from modern Unix shells.
|
||||
--
|
||||
-- * @\\@ escapes a character that might otherwise have special
|
||||
-- meaning. For a literal @\"\\\"@ character, use @\"\\\\\"@.
|
||||
--
|
||||
-- There are two extensions to the traditional glob syntax, taken from
|
||||
-- modern Unix shells.
|
||||
--
|
||||
-- * @**@ matches everything, including a directory separator.
|
||||
--
|
||||
-- * @(/s1/|/s2/|/.../)@ matches any of the strings /s1/, /s2/, etc.
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
import Control.Monad
|
||||
import Codec.Compression.GZip
|
||||
import qualified Data.ByteString.Char8 as S
|
||||
import qualified Data.ByteString.Lazy as L
|
||||
import System.FilePath
|
||||
import System.FilePath.Find
|
||||
import System.FilePath.Glob
|
||||
import System.FilePath.Manip
|
||||
import Text.Regex.Posix ((=~))
|
||||
|
||||
|
@ -68,3 +71,12 @@ recGrep :: String -> FilePath -> IO [(FilePath, Int, S.ByteString)]
|
|||
recGrep pat top = find always (fileType ==? RegularFile) top >>=
|
||||
mapM ((,,) >>= flip grepFile pat) >>=
|
||||
return . concat
|
||||
|
||||
|
||||
-- Decompress all gzip files matching a fixed glob pattern, and return
|
||||
-- the results as a single huge lazy ByteString.
|
||||
|
||||
decomp :: IO L.ByteString
|
||||
|
||||
decomp = namesMatching "*/*.gz" >>=
|
||||
fmap L.concat . mapM (fmap decompress . L.readFile)
|
||||
|
|
Loading…
Add table
Reference in a new issue