Add Glob module, update docs.
This commit is contained in:
parent
cac0d2da66
commit
8b28c0feee
5 changed files with 110 additions and 3 deletions
|
@ -13,6 +13,7 @@ Build-Depends: base, filepath, mtl, unix
|
||||||
GHC-Options: -Wall -O2
|
GHC-Options: -Wall -O2
|
||||||
Exposed-Modules:
|
Exposed-Modules:
|
||||||
System.FilePath.Find,
|
System.FilePath.Find,
|
||||||
|
System.FilePath.Glob,
|
||||||
System.FilePath.GlobPattern,
|
System.FilePath.GlobPattern,
|
||||||
System.FilePath.Manip
|
System.FilePath.Manip
|
||||||
Extra-Source-Files: README
|
Extra-Source-Files: README
|
||||||
|
|
22
README
22
README
|
@ -4,6 +4,28 @@ FileManip: expressive file manipulation
|
||||||
This package provides functions and combinators for searching,
|
This package provides functions and combinators for searching,
|
||||||
matching, and manipulating files.
|
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:
|
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/.
|
-- * @[!/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
|
-- * @\\@ escapes a character that might otherwise have special
|
||||||
-- meaning. For a literal @\"\\\"@ character, use @\"\\\\\"@.
|
-- 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.
|
-- * @**@ matches everything, including a directory separator.
|
||||||
--
|
--
|
||||||
-- * @(/s1/|/s2/|/.../)@ matches any of the strings /s1/, /s2/, etc.
|
-- * @(/s1/|/s2/|/.../)@ matches any of the strings /s1/, /s2/, etc.
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
import Control.Monad
|
import Control.Monad
|
||||||
|
import Codec.Compression.GZip
|
||||||
import qualified Data.ByteString.Char8 as S
|
import qualified Data.ByteString.Char8 as S
|
||||||
|
import qualified Data.ByteString.Lazy as L
|
||||||
import System.FilePath
|
import System.FilePath
|
||||||
import System.FilePath.Find
|
import System.FilePath.Find
|
||||||
|
import System.FilePath.Glob
|
||||||
import System.FilePath.Manip
|
import System.FilePath.Manip
|
||||||
import Text.Regex.Posix ((=~))
|
import Text.Regex.Posix ((=~))
|
||||||
|
|
||||||
|
@ -68,3 +71,12 @@ recGrep :: String -> FilePath -> IO [(FilePath, Int, S.ByteString)]
|
||||||
recGrep pat top = find always (fileType ==? RegularFile) top >>=
|
recGrep pat top = find always (fileType ==? RegularFile) top >>=
|
||||||
mapM ((,,) >>= flip grepFile pat) >>=
|
mapM ((,,) >>= flip grepFile pat) >>=
|
||||||
return . concat
|
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
Add a link
Reference in a new issue