System IO Modules
Table of Contents
1 System IO Modules
1.1 Overview
1.2 System.IO
1.2.1 Overview
System.IO
Standard File Handlers:
- stdin
- stdout
- stderr
IOMode
- ReadMode
- WriteMode
- AppendMode
- ReadWriteMode
1.2.2 Read and Write File
> :t putStrLn putStrLn :: String -> IO () > -- Bind operator -- > :t (>>=) (>>=) :: Monad m => m a -> (a -> m b) -> m b > -- Reverse bind operator -- -- > :t (=<<) (=<<) :: Monad m => (a -> m b) -> m a -> m b > > :t readFile "/etc/lsb-release" readFile "/etc/lsb-release" :: IO String > > readFile "/etc/lsb-release" "DISTRIB_ID=Ubuntu\nDISTRIB_RELEASE=15.04 ..." > readFile "/etc/lsb-release" >>= putStrLn DISTRIB_ID=Ubuntu DISTRIB_RELEASE=15.04 DISTRIB_CODENAME=vivid DISTRIB_DESCRIPTION="Ubuntu 15.04" > putStrLn =<< readFile "/etc/lsb-release" DISTRIB_ID=Ubuntu DISTRIB_RELEASE=15.04 DISTRIB_CODENAME=vivid DISTRIB_DESCRIPTION="Ubuntu 15.04" -- This code must be loaded from a file: -- showFile fileName = do content <- readFile fileName putStrLn content -- This code can be pasted in the repl. -- > let showFile3 fileName = do { content <- readFile fileName ; putStrLn content } > > :t showFile3 showFile3 :: FilePath -> IO () > showFile "/etc/lsb-release" DISTRIB_ID=Ubuntu DISTRIB_RELEASE=15.04 DISTRIB_CODENAME=vivid DISTRIB_DESCRIPTION="Ubuntu 15.04" -- Dessugarized. -- > let showFile2 fileName = readFile fileName >>= \content -> putStrLn content > > showFile2 "/etc/lsb-release" DISTRIB_ID=Ubuntu DISTRIB_RELEASE=15.04 DISTRIB_CODENAME=vivid DISTRIB_DESCRIPTION="Ubuntu 15.04"
1.2.3 Explore System.IO
> import qualified System.IO as SIO > -- Type tab to list the module -- > SIO. > Display all 106 possibilities? (y or n) SIO.AbsoluteSeek SIO.hPutChar SIO.AppendMode SIO.hPutStr SIO.BlockBuffering SIO.hPutStrLn SIO.BufferMode SIO.hReady SIO.CRLF SIO.hSeek > :t SIO.readFile SIO.readFile :: FilePath -> IO String > -- File handler / File descriptor (Unix File descriptor) -- > let fd = SIO.openFile "/etc/lsb-release" SIO.ReadMode > :t SIO.openFile SIO.openFile :: FilePath -> SIO.IOMode -> IO SIO.Handle > > fd {handle: /etc/lsb-release} > :t fd fd :: IO SIO.Handle > -- Return a line from file descritor -- > :t SIO.hGetLine SIO.hGetLine :: SIO.Handle -> IO String > > fd >>= SIO.hGetLine "DISTRIB_ID=Ubuntu" > fd >>= SIO.hGetLine "DISTRIB_ID=Ubuntu" >
1.2.4 Read three Lines from a file.
import qualified System.IO as SIO readThreeLines fileName = do fd <- SIO.openFile fileName SIO.ReadMode line1 <- SIO.hGetLine fd line2 <- SIO.hGetLine fd line3 <- SIO.hGetLine fd SIO.hClose fd return [line1, line2, line3] -- -------------------------------- - --------------------------------- > :paste > readThreeLines "/etc/lsb-release" ["DISTRIB_ID=Ubuntu","DISTRIB_RELEASE=15.04","DISTRIB_CODENAME=vivid"] > > :t readThreeLines "/etc/lsb-release" readThreeLines "/etc/lsb-release" :: IO [String]
1.2.5 Test the module System.IO
import qualified System.IO as SIO > let fd = SIO.openFile "/etc/lsb-release" SIO.ReadMode > :t fd fd :: IO SIO.Handle > > fd >>= SIO.hGetContents "DISTRIB_ID=Ubuntu\nDISTRIB_RELEASE=15.04\nDISTRIB_CODENAME ... ... " > > fd >>= SIO.hGetContents >>= putStrLn DISTRIB_ID=Ubuntu DISTRIB_RELEASE=15.04 DISTRIB_CODENAME=vivid DISTRIB_DESCRIPTION="Ubuntu 15.04" let readLine fd = SIO.hGetLine fd >>= \line -> return (line, fd) > :t readLine readLine :: SIO.Handle -> IO (String, SIO.Handle) > > fd >>= readLine ("DISTRIB_ID=Ubuntu",{handle: /etc/lsb-release}) > > fd >>= readLine ("DISTRIB_ID=Ubuntu",{handle: /etc/lsb-release}) > > let readTwoLines fd = readLine fd >>= \(line1, fd) -> readLine fd >>= \(line2, fd) -> return ([line1, line2], fd) > fd >>= readTwoLines (["DISTRIB_ID=Ubuntu","DISTRIB_RELEASE=15.04"],{handle: /etc/lsb-release}) >
1.2.6 Read all lines of a file
import qualified System.IO as SIO fdtest = SIO.openFile "/etc/lsb-release" SIO.ReadMode readLineState :: SIO.Handle -> IO (String, SIO.Handle) readLineState fd = do line <- SIO.hGetLine fd return (line, fd) readLinesAux :: SIO.Handle -> [String] -> IO [String] readLinesAux fd lines = do (lin, fdnext) <- readLineState fd isEOF <- SIO.hIsEOF fdnext if isEOF then return (reverse lines) else readLinesAux fdnext (lin:lines) readLines :: SIO.Handle -> IO [String] readLines fd = readLinesAux fd [] readLinesFromFile :: String -> IO [String] readLinesFromFile fileName = do handle <- SIO.openFile fileName SIO.ReadMode lines <- readLines handle SIO.hClose handle return lines makeReader :: (SIO.Handle -> IO out) -> (SIO.Handle -> IO (out, SIO.Handle)) makeReader reader fd = do out <- reader fd return (out, fd) applyFileReaderAux :: (SIO.Handle -> IO (out, SIO.Handle)) -> SIO.Handle -> [out] -> IO [out] applyFileReaderAux fileReader fd acc = do (output, fdnext) <- fileReader fd isEOF <- SIO.hIsEOF fdnext if isEOF then return (reverse acc) else applyFileReaderAux fileReader fdnext (output:acc) applyFileReader :: (SIO.Handle -> IO (out, SIO.Handle)) -> SIO.Handle -> IO [out] applyFileReader fileReader fd = applyFileReaderAux fileReader fd []
Repl tests:
> :t fdtest fdtest :: IO SIO.Handle > > :t readLineState readLineState :: SIO.Handle -> IO (String, SIO.Handle) > > fdtest >>= readLineState ("DISTRIB_ID=Ubuntu",{handle: /etc/lsb-release}) > > readLineState =<< fdtest ("DISTRIB_ID=Ubuntu",{handle: /etc/lsb-release}) > > fdtest >>= \fd -> readLinesAux fd [] ["DISTRIB_ID=Ubuntu","DISTRIB_RELEASE=15.04","DISTRIB_CODENAME=vivid"] > > :t fdtest >>= \fd -> readLinesAux fd [] fdtest >>= \fd -> readLinesAux fd [] :: IO [String] > > fdtest >>= readLines ["DISTRIB_ID=Ubuntu","DISTRIB_RELEASE=15.04","DISTRIB_CODENAME=vivid"] > > readLinesFromFile "/etc/lsb-release" ["DISTRIB_ID=Ubuntu","DISTRIB_RELEASE=15.04","DISTRIB_CODENAME=vivid"] > > :t SIO.withFile SIO.withFile :: FilePath -> SIO.IOMode -> (SIO.Handle -> IO r) -> IO r > > SIO.withFile "/etc/lsb-release" SIO.ReadMode readLines ["DISTRIB_ID=Ubuntu","DISTRIB_RELEASE=15.04","DISTRIB_CODENAME=vivid"] > > :t SIO.withFile "/etc/lsb-release" SIO.ReadMode readLines SIO.withFile "/etc/lsb-release" SIO.ReadMode readLines :: IO [String] -- ------------------------ > > fd <- SIO.openFile "/etc/lsb-release" SIO.ReadMode > :t fd fd :: SIO.Handle > > fd {handle: /etc/lsb-release} > > :t readLineState readLineState :: SIO.Handle -> IO (String, SIO.Handle) > applyFileReaderAux readLineState fd [] ["DISTRIB_ID=Ubuntu","DISTRIB_RELEASE=15.04","DISTRIB_CODENAME=vivid"] > > let charReader = makeReader SIO.hGetChar > :t charReader charReader :: SIO.Handle -> IO (Char, SIO.Handle) > > applyFileReader charReader fd "DISTRIB_ID=Ubuntu\nDISTRIB_RELEASE=15.04\nDISTRIB_CODENAME= ..." > > fd <- SIO.openFile "/etc/lsb-release" SIO.ReadMode > applyFileReader (makeReader SIO.hGetLine) fd ["DISTRIB_ID=Ubuntu","DISTRIB_RELEASE=15.04","DISTRIB_CODENAME=vivid"] > > applyFileReader (makeReader SIO.hGetLine) fd *** Exception: /etc/lsb-release: hGetLine: end of file >
1.3 System.Environment
Miscellaneous information about the system environment.
> import qualified System.Environment as E > -- returns a list of the program's command line arguments -- (not including the program name). -- > E.getArgs [] > :t E.getArgs E.getArgs :: IO [String] > -- Computation getProgName returns the name of the program as it was invoked. -- > :t E.getProgName E.getProgName :: IO String > > E.getProgName "<interactive>" > -- Returns the absolute pathname of the current executable. -- Note that for scripts and interactive sessions, this is -- the path to the interpreter (e.g. ghci.) > :t E.getExecutablePath E.getExecutablePath :: IO FilePath > > E.getExecutablePath "/usr/lib/ghc/lib/ghc" > > path <- E.getExecutablePath > :t path path :: FilePath > path "/usr/lib/ghc/lib/ghc" > -- Computation getEnv var returns the value of the environment -- variable var. For the inverse, POSIX users can use putEnv. -- -- > :t E.getEnv E.getEnv :: String -> IO String > > E.getEnv "HOME" "/home/tux" > > E.getEnv "PATH" "/home/tux/.cask/bin:/opt/bin:/home/tux/.opam/4.02.1/bin:/home/tux/bin ..." > E.getEnv "DONTEXISTS" *** Exception: DONTEXISTS: getEnv: does not exist (no environment variable) > -- Return the value of the environment variable var, -- or Nothing if there is no such value. -- > :t E.lookupEnv E.lookupEnv :: String -> IO (Maybe String) > > E.lookupEnv "JDK_HOME" Just "/opt/java" > > E.lookupEnv "JDK_HOMEasdasd" Nothing > -- getEnvironment retrieves the entire environment as a list of (key,value) pairs. > :t E.getEnvironment E.getEnvironment :: IO [(String, String)] > > E.getEnvironment >>= \lst -> return (take 10 lst) [("USER","tux"),("LANGUAGE","en_US"),("LC_TIME","pt_BR.UTF-8"),("COMP_WORDBREAKS"," \t\n\"'><;|&(:"),("XDG_SEAT","seat0"),("SSH_AGENT_PID","15614"),("XDG_SESSION_TYPE","x11"),("SHLVL","1"),("HOME","/home/tux"),("QT4_IM_MODULE","xim")] > > :t mapM_ mapM_ :: Monad m => (a -> m b) -> [a] -> m () > > :t E.getEnvironment >>= \rows -> mapM_ (\row -> putStrLn (show row)) rows E.getEnvironment >>= \rows -> mapM_ (\row -> putStrLn (show row)) rows :: IO () > > E.getEnvironment >>= \rows -> mapM_ (\row -> putStrLn (show row)) rows ("USER","tux") ("LANGUAGE","en_US") ("LC_TIME","pt_BR.UTF-8") ("XDG_SEAT","seat0") ("SSH_AGENT_PID","15614") ("XDG_SESSION_TYPE","x11") ("SHLVL","1") ...
1.4 System.Directory
> import qualified System.Directory as D > -- createDirectory dir creates a new directory dir which is initially -- empty, or as near to empty as the operating system allows. > D.createDirectory "/tmp/testdir" > D.createDirectory "/tmp/testdir" *** Exception: /tmp/testdir: createDirectory: already exists (File exists) > {- createDirectoryIfMissing Source :: Bool Create its parents too? -> FilePath The path to the directory you want to make -> IO () -} > D.createDirectoryIfMissing True "/tmp/tree1/1/2/3/4" > > D.createDirectoryIfMissing True "/tmp/tree1/1/2/3/4" > > D.createDirectoryIfMissing True "/tree1/1/2/3/4" *** Exception: /tree1: createDirectory: permission denied (Permission denied) > -- removeDirectory dir removes an existing directory dir. > D.removeDirectory "/tmp/testdir" > D.removeDirectory "/tmp/testdir" *** Exception: /tmp/testdir: removeDirectory: does not exist (No such file or directory) > -- Similar to listDirectory, but always includes the special entries -- (. and ..). (This applies to Windows as well.) > :t D.getDirectoryContents "/boot" D.getDirectoryContents "/boot" :: IO [FilePath] > > fmap (take 2) (D.getDirectoryContents "/boot" ) ["initrd.img-3.19.0-37-generic","config-3.19.0-21-generic"] > > take 2 <$> D.getDirectoryContents "/boot" ["initrd.img-3.19.0-37-generic","config-3.19.0-21-generic"] > > D.getDirectoryContents "/boot" >>= mapM_ putStrLn initrd.img-3.19.0-37-generic config-3.19.0-21-generic System.map-3.19.0-39-generic abi-3.19.0-18-generic ... > D.getDirectoryContents "/boot" >>= \rows -> return (take 10 rows) >>= mapM_ putStrLn initrd.img-3.19.0-37-generic config-3.19.0-21-generic System.map-3.19.0-39-generic ... > > let displayDir path = D.getDirectoryContents path >>= \rows -> return (take 10 rows) >>= mapM_ putStrLn > > displayDir "/boot" initrd.img-3.19.0-37-generic config-3.19.0-21-generic System.map-3.19.0-39-generic ... -- Obtain the current working directory as an absolute path. -- > :t D.getCurrentDirectory D.getCurrentDirectory :: IO FilePath > > D.getCurrentDirectory "/home/tux" > -- Change the working directory to the given path. -- > :t D.setCurrentDirectory D.setCurrentDirectory :: FilePath -> IO () > -- Returns the current user's home direct -- > D.getHomeDirectory "/home/tux" > > D.getUserDocumentsDirectory "/home/tux" > > D.getTemporaryDirectory "/tmp" > -- copyFile old new copies the existing file from old to new. If the -- new file already exists, it is atomically replaced by the old -- file. Neither path may refer to an existing directory. The -- permissions of old are copied to new, if possible. > :t D.copyFile D.copyFile :: FilePath -> FilePath -> IO () > > > D.copyFile "/etc/lsb-release" "/tmp/test.txt" > > readFile "/tmp/test.txt" >>= putStrLn DISTRIB_ID=Ubuntu DISTRIB_RELEASE=15.04 DISTRIB_CODENAME=vivid DISTRIB_DESCRIPTION="Ubuntu 15.04" {- removeFile file removes the directory entry for an existing file file, where file is not itself a directory. The implementation may specify additional constraints which must be satisfied before a file can be removed (e.g. the file may not be in use by other processes). -} > :t D.removeFile D.removeFile :: FilePath -> IO () > > D.removeFile "/tmp/test.txt" > D.removeFile "/tmp/test.txt" *** Exception: /tmp/test.txt: removeLink: does not exist (No such file or directory) > {- Given an executable file name, searches for such file in the directories listed in system PATH. The returned value is the path to the found executable or Nothing if an executable with the given name was not found. For example (findExecutable "ghc") gives you the path to GHC. -} > D.findExecutable "java" Just "/opt/bin/java" > > D.findExecutable "javac" Just "/opt/java/bin/javac" > D.findExecutable "javaasdas" Nothing > {- Given a file name, searches for the file and returns a list of all occurences that are executable. -} > D.findExecutables "java" ["/opt/bin/java","/opt/bin/java","/usr/bin/java","/opt/java/bin/java"] > > D.findExecutables "ghci" ["/usr/bin/ghci"] > {- Given a file name, searches for the file on the given paths and returns a list of all occurences that are executable. -} > :t D.findFile D.findFile :: [FilePath] -> String -> IO (Maybe FilePath) > > D.findFiles ["/opt/bin", "/usr/bin"] "java" ["/opt/bin/java","/usr/bin/java"] > > D.findFiles ["/opt/bin", "/usr/bin"] "jasadva" [] -- The operation doesFileExist returns True if the argument file -- exists and is not a directory, and False otherwise. > D.doesFileExist "/etc/fstab" True > > D.doesFileExist "/etc/fstaASDb" False > {- The operation doesDirectoryExist returns True if the argument file exists and is either a directory or a symbolic link to a directory, and False otherwise. -} > > D.doesDirectoryExist "/etc" True > D.doesDirectoryExist "/etc/Dummy" False > -- The getPermissions operation returns the permissions for the file or directory. -- > D.getPermissions "/etc/fstab" Permissions {readable = True, writable = False, executable = False, searchable = False} >
1.5 System.Info
Information about the characteristics of the host system lucky enough to run your program.
> import qualified System.Info as I > > I.os "linux" > > I.arch "i386" > > I.compilerName "ghc" > > I.compilerVersion Version {versionBranch = [7,6], versionTags = []} > > :t I.compilerVersion I.compilerVersion :: Data.Version.Version >
1.6 System.Process
> import qualified System.Process as P > {- Creates a new process to run the specified command with the given arguments, and wait for it to finish. If the command returns a non-zero exit code, an exception is raised. -} > P.callProcess "date" [] Ter Fev 23 12:29:42 BRT 2016 > :t P.callProcess "date" [] P.callProcess "date" [] :: IO () > > P.callProcess "dadsfte" [] *** Exception: dadsfte: callProcess: runInteractiveProcess: exec: does not exist (No such file or directory) > > P.callProcess "uname" ["-a"] Linux tuxhorse 3.19.0-39-generic #44-Ubuntu SMP Tue Dec 1 14:38:23 UTC 2015 i686 i686 i686 GNU/Linux > {- Creates a new process to run the specified shell command. If the command returns a non-zero exit code, an exception is raised. -} > P.callCommand "uname -a" Linux tuxhorse 3.19.0-39-generic #44-Ubuntu SMP Tue Dec 1 14:38:23 UTC 2015 i686 i686 i686 GNU/Linux > > :t P.callCommand P.callCommand :: String -> IO () > {- readProcess forks an external process, reads its standard output strictly, blocking until the process terminates, and returns the output string. The external process inherits the standard error. readProcess Source :: FilePath Filename of the executable (see RawCommand for details) -> [String] any arguments -> String standard input -> IO String stdout -} > :t P.readProcess P.readProcess :: FilePath -> [String] -> String -> IO String > > P.readProcess "uname" ["-r"] "" "3.19.0-39-generic\n" > > kernelVersion <- P.readProcess "uname" ["-r"] "" > kernelVersion "3.19.0-39-generic\n" > :t kernelVersion kernelVersion :: String > {- readProcessWithExitCode Source :: FilePath Filename of the executable (see RawCommand for details) -> [String] any arguments -> String standard input -> IO (ExitCode, String, String) exitcode, stdout, stderr -} > > P.readProcessWithExitCode "uname" ["-a"] "" (ExitSuccess,"Linux tuxhorse 3.19.0-39-generic #44-Ubuntu SMP Tue Dec 1 14:38:23 UTC 2015 i686 i686 i686 GNU/Linux\n","") > > P.readProcessWithExitCode "uname" ["-x"] "" (ExitFailure 1,"","uname: invalid option -- 'x'\nTry 'uname --help' for more information.\n") > > P.readProcessWithExitCode "unasdame" ["-x"] "" *** Exception: unasdame: readCreateProcessWithExitCode: runInteractiveProcess: exec: does not exist (No such file or directory) >