ContentsIndex
HsShellScript
Contents
Command Line Arguments
Paths and Directories
Parsing and Composing Paths
Symbolic Links
Manipulating Files
Interfaces to Some Specific External Commands
Calling External Programs
Running a Subroutine in a Separate Process
About the exec Functions
Functions for Calling External Programs
Redirecting Input and Output
Pipes
File Descriptors in Pipes
Pipe Creation Functions
Shell-like Quoting
Creating temporary files and directories
Reading mount information
Colorful logging and error reporting
Miscellaneous
Error Handling
Reexported Standard Library Stuff for Exception Handling
Synopsis
module HsShellScript.Args
mkdir :: String -> IO ()
rmdir :: String -> IO ()
pwd :: IO String
cd :: String -> IO ()
realpath :: String -> IO String
realpath_s :: String -> IO String
path_exists :: String -> IO Bool
path_exists' :: String -> IO Bool
is_file :: String -> IO Bool
is_dir :: String -> IO Bool
with_wd :: FilePath -> IO a -> IO a
slice_path :: String -> [String]
unslice_path :: [String] -> String
normalise_path :: String -> String
slice_filename :: String -> [String]
slice_filename' :: String -> [String]
unslice_filename :: [String] -> String
split_path :: String -> (String, String)
dir_part :: String -> String
filename_part :: String -> String
unsplit_path :: (String, String) -> String
unsplit_parts :: [String] -> String
split_filename :: String -> (String, String)
split_filename' :: String -> (String, String)
unsplit_filename :: (String, String) -> String
split3 :: String -> (String, String, String)
unsplit3 :: (String, String, String) -> String
test_suffix :: String -> String -> Maybe String
absolute_path :: String -> IO String
absolute_path_by :: String -> String -> String
absolute_path' :: String -> String -> String
guess_dotdot_comps :: [String] -> Maybe [String]
guess_dotdot :: String -> Maybe String
is_symlink :: String -> IO Bool
symlink :: String -> String -> IO ()
readlink :: String -> IO String
readlink' :: String -> IO String
rm :: String -> IO ()
chmod :: [String] -> IO ()
chown :: [String] -> IO ()
cp :: String -> String -> IO ()
mv :: String -> String -> IO ()
rename :: String -> String -> IO ()
rename_mv :: FilePath -> FilePath -> IO ()
force_rename :: String -> String -> IO ()
force_mv :: String -> String -> IO ()
force_rename_mv :: FilePath -> FilePath -> IO ()
force_cmd :: (String -> String -> IO ()) -> String -> String -> IO ()
force_writeable :: String -> IO a -> IO a
force_writeable2 :: String -> IO (String, a) -> IO a
getFileStatus' :: FilePath -> IO FileStatus
fileAccess' :: FilePath -> Bool -> Bool -> Bool -> IO Bool
setFileMode' :: FilePath -> FileMode -> IO ()
mt_status :: IO (Int, Int)
fdupes :: [String] -> [String] -> IO [[[String]]]
du :: (Integral int, Read int) => int -> String -> IO int
call :: IO a -> IO ()
spawn :: IO a -> IO ProcessID
run :: FilePath -> [String] -> IO ()
exec :: String -> [String] -> IO a
execp :: String -> [String] -> IO a
exece :: String -> [String] -> [(String, String)] -> IO a
execpe :: String -> [String] -> [(String, String)] -> IO a
echo :: (FilePath -> [String] -> IO ()) -> FilePath -> [String] -> IO ()
silently :: IORef String -> IO () -> IO ()
system_throw :: String -> IO ()
execute_file :: FilePath -> Bool -> [String] -> Maybe [(String, String)] -> IO a
child :: IO a -> IO b
(->-) :: IO a -> FilePath -> IO a
(->>-) :: IO a -> FilePath -> IO a
(=>-) :: IO a -> FilePath -> IO a
(=>>-) :: IO a -> FilePath -> IO a
(-<-) :: IO a -> FilePath -> IO a
(-&>-) :: IO a -> FilePath -> IO a
(-&>>-) :: IO a -> FilePath -> IO a
err_to_out :: IO a -> IO a
out_to_err :: IO a -> IO a
(-|-) :: IO a -> IO b -> IO a
(=|-) :: IO a -> IO b -> IO a
(-|=) :: IO a -> IO b -> IO b
(=|=) :: IO a -> IO b -> IO b
pipe_to :: String -> IO a -> IO ()
h_pipe_to :: IO a -> IO (Handle, ProcessID)
pipe_from :: IO a -> IO String
lazy_pipe_from :: IO a -> IO (String, ProcessID)
h_pipe_from :: IO a -> IO (Handle, ProcessID)
pipe_from2 :: IO a -> IO (String, ProcessStatus)
lazy_pipe_from2 :: IO a -> IO (String, ProcessID)
h_pipe_from2 :: IO a -> IO (Handle, ProcessID)
pipes :: IO a -> Bool -> Bool -> Bool -> IO (Maybe Handle, Maybe Handle, Maybe Handle, ProcessID)
shell_command :: String -> [String] -> String
shell_quote :: String -> String
quote0 :: String -> String
quote :: String -> String
tmp_file :: String -> IO FilePath
tmp_dir :: String -> IO FilePath
temp_file :: Int -> String -> String -> IO FilePath
temp_dir :: Int -> String -> String -> IO FilePath
temp_path :: Int -> String -> String -> IO FilePath
with_tmp_file :: String -> (Handle -> IO a) -> IO a
with_tmp_dir :: String -> (FilePath -> IO a) -> IO a
with_temp_file :: Int -> String -> String -> (Handle -> IO a) -> IO a
with_temp_dir :: Int -> String -> String -> (FilePath -> IO a) -> IO a
data Mntent = Mntent {
mnt_fsname :: String
mnt_dir :: String
mnt_type :: String
mnt_opts :: String
mnt_freq :: Int
mnt_passno :: Int
}
read_mounts :: String -> IO [Mntent]
read_mtab :: IO [Mntent]
read_fstab :: IO [Mntent]
logm :: String -> IO ()
logm_ :: String -> IO ()
errm :: String -> IO ()
errm_ :: String -> IO ()
zeros :: Int -> Int -> String
chomp :: String -> String
lazy_contents :: String -> IO String
contents :: String -> IO String
mainwrapper :: IO a -> IO a
errno :: IO Errno
strerror :: Errno -> IO String
perror' :: Errno -> String -> IO ()
perror :: String -> IO ()
abort :: Typeable err => (err -> String) -> IO a -> IO a
_exit :: Int -> IO a
failIO :: String -> IO a
exitcode :: IO () -> IO ExitCode
throwErrno' :: String -> Maybe Handle -> Maybe FilePath -> IO a
show_ioerror :: IOError -> String
fill_in_filename :: String -> IO a -> IO a
fill_in_location :: String -> IO a -> IO a
add_location :: String -> IO a -> IO a
Exception
IOException
ArithException
ArrayException
AsyncException
throw
ioError
throwTo
catchJust
handle
handleJust
tryJust
evaluate
ioErrors
arithExceptions
errorCalls
dynExceptions
assertions
asyncExceptions
userErrors
throwDyn
throwDynTo
catchDyn
block
unblock
assert
finally
toDyn
fromDyn
fromDynamic
dynApply
dynApp
Typeable
TypeRep
TyCon
mkTyCon
mkAppTy
mkFunTy
applyTy
Permissions
createDirectory
removeDirectory
renameDirectory
getDirectoryContents
getCurrentDirectory
setCurrentDirectory
removeFile
renameFile
doesFileExist
doesDirectoryExist
getPermissions
setPermissions
getModificationTime
fileAccess
getProcessStatus
Command Line Arguments
Command line arguments are handled by the module HsShellScript.Args, which is reexported by HsShellScript.
module HsShellScript.Args
Paths and Directories
mkdir
:: Stringpath
-> IO ()
Create directory. This is a shorthand to Directory.createDirectory from the Haskell standard library. In case of an error, the path is included in the IOError, which GHC's implementation neglects to do.
rmdir
:: Stringpath
-> IO ()
Remove directory. This is Directory.removeDirectory from the Haskell standard library. In case of an error, the path is included in the IOError, which GHC's implementation neglects to do.
pwd :: IO String
Get program start working directory. This is the PWD environent variable, which is kept by the shell (bash, at least). It records the directory path in which the program has been started. Symbolic links in this path aren't expanded. In this way, it differs from getCurrentDirectory from the Haskell standard library.
cd
:: Stringpath
-> IO ()

Change directory. This is an alias for Directory.setCurrentDirectory from the Haskell standard library. In case of an error, the path is included in the IOError, which GHC's implementation neglects to do.

Note that this command is subtly different from the shell's cd command. It changes the process' working directory. This is always a realpath. Symlinks are expanded. The shell, on the other hand, keeps track of the current working directory separately, in a different way: symlinks are not expanded. The shell's idea of the working directory is different from the working directory which a process has.

This means that the same sequence of cd commands, when done in a real shell script, will lead into the same directory. But the working directory as reported by the shell's pwd command may differ from the corresponding one, reported by getCurrentDirectory.

(When talking about the "shell", I'm talking about bash, regardless of whether started as /bin/bash or in compatibility mode, as /bin/sh. I presume it's the standard behavior for the POSIX standard shell.)

See pwd.

realpath
:: Stringpath
-> IO Stringnoramlized, absolute path, with symbolic links expanded
Do a call to the realpath(3) system library function. This makes the path absolute, normalizes it and expands all symbolic links. In case of an error, an IOError is thrown.
realpath_s
:: Stringpath
-> IO Stringnoramlized, absolute path, with symbolic links not expanded

Return the normalised, absolute version of a specified path. The path is made absolute with the current working directory, and is syntactically normalised afterwards. This is the same as what the realpath program reports with the -s option. It's almost the same as what it reports when called from a shell. The difference lies in the shell's idea of the current working directory. See cd for details.

See cd, normalise_path.

path_exists
:: StringPath
-> IO BoolWhether the path exists in the file system
Test for the existence of a path. This is the disjunction of Directory.doesDirectoryExist and Directory.doesFileExist. For an dangling symlink, this will return False.
path_exists'
:: StringPath
-> IO BoolWhether the path exists in the file system
Test for the existence of a path. This uses System.Posix.Files.getFileStatus to determine whether the path exists in any form in the file system. For a dangling symlink, the result is True.
is_file
:: StringPath
-> IO BoolWhether the path exists and points to a file.
Test if path points to a file. This is a shortcut for Directory.doesFileExist.
is_dir
:: StringPath
-> IO BoolWhether the path exists and points to a directory.
Test if path points to a directory. This will return True for a symlink pointing to a directory. It's a shortcut for Directory.doesDirectoryExist.
with_wd
:: FilePathNew working directory
-> IO aAction to run
-> IO a
Change the working directory temporarily. This executes the specified IO action with a new working directory, and restores it afterwards (exception-safely).
Parsing and Composing Paths
slice_path
:: StringThe path to be broken to components.
-> [String]List of path components.

Split a path in components. Repeated "/" characters don't lead to empty components. "." path components are removed. If the path is absolute, the first component will start with "/". ".." components are left intact. They can't be simply removed, because the preceding component might be a symlink. In this case, realpath is probably what you need.

The case that the path is empty is treated like ".", yielding an empty path components list.

Examples:

slice_path "/"        = ["/"]
slice_path "/foo/bar" = ["/foo","bar"]
slice_path "..//./"   = [".."]
slice_path "."        = []
slice_path ""         = []

See unslice_path, realpath, realpath_s.

unslice_path
:: [String]List of path components
-> StringThe path which consists of the supplied path components

Form a path from path components. This isn't the inverse of slice_path, since unslice_path . slice_path normalises the path.

unslice_path [] = "."
unslice_path cs = concat (intersperse "/" cs)

See slice_path, unsplit_parts.

normalise_path
:: StringPath to be normalised
-> StringPath in normalised form

Normalise a path. This is done by reducing repeated / characters to one, and removing . path components. .. path components are left intact, because of possible symlinks.

Note that the normalised path isn't 100% equivalent to the original one. Any trailing slash is removed. When the last path component is a symbolic link, then both paths denote the same thing, except for in the context of the readlink call. It will fail when the trailing slash is present (because then the path denotes the directory which the link points to), but it will succeed when it is absent.

normalise_path = unslice_path . slice_path

See unslice_path, slice_path.

slice_filename
:: StringPath
-> [String]List of components the file name is made up of

Split a file name in components. This are the base file name and the suffixes, which are separated by dots. If the name starts with a dot, it is regarded as part of the base name. The result is a list of file name components. The filename may be a path. In this case, everything up to the last path component will be returned as part of the base file name. The path gets normalised thereby.

No empty suffixes are returned. If the file name contains several consecutive dots, they are regared as part of the preceding file name component.

Concateneting the name components and adding dots, reproduces the original name, with a normalised path: concat . intersperse "." . slice_filename == normalise.

Note that the last path component might be "..". Then it is not possible to deduce the refered directory's name from the path. An IO action for getting the real path is then necessary.

Examples:

slice_filename "a.b//./.foo.tar.gz" = ["a.b/.foo","tar","gz"]
slice_filename ".x..y."             = [".x.", "y."]

See unslice_filename, slice_filename'.

slice_filename'
:: StringFile name without path
-> [String]List of components the file name is made up of

This is a variant of slice_filename. It is like slice_filename, except for being more efficient, and the filename must not contain any preceding path, since this case isn't considered.

See slice_filename, unslice_filename.

unslice_filename
:: [String]List of file name components
-> StringName of the file which consists of the supplied components

Form file name from file name components, interspersing dots. This is the inverse of slice_filename, except for normalisation of any path.

 unslice_filename = concat . intersperse "."

See slice_filename.

split_path
:: StringPath to be split
-> (String, String)Directory and file name components of the path. The directory path is normalized.

Split a path in directory and file name. Only in the case that the supplied path is empty, both parts are empty strings. Otherwise, "." is filled in for the corresponding part, if necessary. Unless the path is empty, concatenating the returned path and file name components with a slash in between, makes a valid path to the file.

split_path splits off the last path component. This isn't the same as the text after the last /.

Note that the last path component might be "..". Then it is not possible to deduce the refered directory's name from the path. Then an IO action for getting the real path is necessary.

Examples:

split_path "/a/b/c"      == ("/a/b", "c")
split_path "foo"         == (".", "foo")
split_path "foo/bar"     == ("foo", "bar")
split_path "foo/.."      == ("foo", "..")
split_path "."           == (".", ".")
split_path ""            == ("", "")
split_path "/foo"        == ("/", "foo")
split_path "foo/"        == (".", "foo")
split_path "foo/."       == (".", "foo")
split_path "foo///./bar" == ("foo", "bar")

See slice_path.

dir_part :: String -> String

Get the directory part of a path.

dir_part = fst . split_path

See split_path.

filename_part :: String -> String

Get the last path component of a path.

filename_part = snd . split_path

Examples:

filename_part "foo/bar" == "bar"
filename_part "."       == "."

See split_path.

unsplit_path
:: (String, String)Directory and file name
-> StringPath formed from the directory and file name parts

Inverse of split_path, except for normalisation.

This forms a path from two parts, and takes care of "." and empty parts. When the two components are in normalised form, then unsplit_path creates a normalised path.

The definition:

unsplit_path ("", "") = ""
unsplit_path (p, q)   = unsplit_parts [p, q]

Examples:

unsplit_path ("", "")     == ""
unsplit_path (".", "")    == "."
unsplit_path (".", ".")   == "."
unsplit_path ("foo", ".") == "foo"

See split_path, slice_path, unsplit_parts.

unsplit_parts
:: [String]List of path parts to concatenate.
-> StringFormed path, which concatenates the parts.

Concatenate a list of path parts. The idea is that you can throw in reasonably formed parts, and get a reasonably formed version of the concatenated path out.

"." parts are removed. Empty parts are treated as "." parts. One leading slash in each of any but the first part is removed. The result is then interspersed with slashes and string wise concatenated. The interior of the parts isn't examined. ".." components aren't treated specially.

Examples:

unsplit_parts []                       == "."
unsplit_parts [""]                     == "."
unsplit_parts ["/"]                    == "/"
unsplit_parts ["/", "foo"]             == "/foo"
unsplit_parts ["", "/foo"]             == "foo"
unsplit_parts ["/foo", "bar"]          == "/foo/bar"
unsplit_parts ["/foo", "/bar"]         == "/foo/bar"
unsplit_parts ["foo/", "bar"]          == "foo//bar"
unsplit_parts ["foo", "", ".", "bar"]  == "foo/bar"
unsplit_parts ["foo", "bar//./baz/"]   == "foo/bar//./baz/"

See unsplit_path, unslice_path, split_path.

split_filename
:: StringPath including the file name to be split
-> (String, String)The normalised path with the file prefix, and the file suffix.

Split a file name in prefix and suffix. If there isn't any suffix in the file name, then return an empty suffix. A dot at the beginning or at the end is not regarded as introducing a suffix.

The last path component is what is being split. This isn't the same as splitting the string at the last dot. For instance, if the file name doesn't contain any dot, dots in previous path component's aren't mistaken as introducing suffixes.

The path part is returned in normalised form. This means, "." components are removed, and multiple "/"s are reduced to one.

Note that there isn't any plausibility check performed on the suffix. If the file name doesn't have a suffix, but happens to contain a dot, then this dot is mistaken as introducing a suffix.

Examples:

split_filename "path/to/foo.bar"                             = ("path/to/foo","bar")
split_filename "path/to/foo"                                 = ("path/to/foo","")
split_filename "/path.to/foo"                                = ("/path.to/foo","")
split_filename "a///./x"                                     = ("a/x","")
split_filename "dir.suffix/./"                               = ("dir","suffix")
split_filename "Photographie, Das 20. Jahrhundert (300 dpi)" = ("Photographie, Das 20", " Jahrhundert (300 dpi)")

See slice_path, split_filename'

split_filename'
:: StringFilename to be split
-> (String, String)Base name and the last suffix

Variant of split_filename. This is a more efficient version of split_filename, for the case that you know the string is is a pure file name without any slashes.

See split_filename.

unsplit_filename
:: (String, String)File name prefix and suffix
-> StringPath

Inverse of split_filename. Concatenate prefix and suffix, adding a dot in between, iff the suffix is not empty. The path part of the prefix is normalised.

See split_filename.

split3
:: StringPath to split
-> (String, String, String)Directory part, base file name part and suffix part
Split a path in directory, base file name and suffix.
unsplit3
:: (String, String, String)Directory part, base file name part and suffix part
-> StringPath consisting of dir, base and suffix
Form path from directory, base file name and suffix parts.
test_suffix
:: StringSuffix to split off
-> StringPath to test
-> Maybe StringPrefix without the suffix or Nothing

Test a path for a specific suffix and split it off.

If the path ends with the suffix, then the result is Just prefix, where prefix is the normalised path without the suffix. Otherwise it's Nothing.

absolute_path
:: StringThe path to be made absolute
-> IO StringAbsulte path

Make a path absolute, using the current working directory.

This makes a relative path absolute with respect to the current working directory. An absolute path is returned unmodified.

The current working directory is determined with getCurrentDirectory which means that symbolic links in it are expanded and the path is normalised. This is different from pwd.

absolute_path_by
:: StringThe directory relative to which the path is made absolute
-> StringThe path to be made absolute
-> StringAbsolute path

Make a path absolute.

This makes a relative path absolute with respect to a specified directory. An absolute path is returned unmodified.

absolute_path'
:: StringThe path to be made absolute
-> StringThe directory relative to which the path is made absolute
-> StringAbsolute path

Make a path absolute.

This makes a relative path absolute with respect to a specified directory. An absolute path is returned unmodified.

The order of the arguments can be confusing. You should rather use absolute_path_by. absolute_path' is included for backwards compatibility.

guess_dotdot_comps
:: [String]List of path components
-> Maybe [String]In case the path could be transformed, the ".."-component free list of path components.
Guess the ".."-component free form of a path, specified as a list of path components, by syntactically removing them, along with the preceding path components. This will produce erroneous results when the path contains symlinks. If the path contains leading ".." components, or more ".." components than preceeding normal components, then the ".." components can't be normalised away. In this case, the result is Nothing.
guess_dotdot
:: StringPath to be normalised
-> Maybe StringIn case the path could be transformed, the normalised, ".."-component free form of the path.

Guess the ".."-component free, normalised form of a path. The transformation is purely syntactic. ".." path components will be removed, along with their preceding path components. This will produce erroneous results when the path contains symlinks. If the path contains leading ".." components, or more ".." components than preceeding normal components, then the ".." components can't be normalised away. In this case, the result is Nothing.

guess_dotdot = fmap unslice_path . guess_dotdot_comps . slice_path
Symbolic Links
is_symlink
:: Stringpath
-> IO BoolWhether the path is a symbolic link.
Determine whether a path is a symbolic link. The result for a dangling symlink is True. In case of an error, a proper IOError is thrown.
symlink
:: Stringcontents of the symlink (from)
-> Stringpath of the symlink (to)
-> IO ()
Make a symbolic link. This is the symlink(2) function. Any error results in an IOError thrown. The path of the intended symlink is included in the IOError and can be accessed with ioeGetFileName from the Haskell standard library IO.
readlink
:: StringPath of the symbolic link
-> IO StringThe link target - where the symbolic link points to
Determine the target of a symbolic link. This uses the readlink(2) system call. The result is a path which is either absolute, or relative to the directory which the symlink is in. In case of an error, an IOError is thrown. The path is included and can be accessed with IO.ioeGetFileName. Note that, if the path to the symlink ends with a slash, this path denotes the directory pointed to, not the symlink. In this case the call to will fail because of "Invalid argument".
readlink'
:: Stringpath of the symbolic link
-> IO Stringtarget; where the symbolic link points to
Determine the target of a symbolic link. This uses the readlink(2) system call. The target is converted, such that it is relative to the current working directory, if it isn't absolute. Note that, if the path to the symlink ends with a slash, this path denotes the directory pointed to, not the symlink. In this case the call to readlink will fail with an IOError because of Invalid argument. In case of any error, a proper IOError is thrown.
Manipulating Files
rm
:: Stringpath
-> IO ()
Remove file. This is Directory.removeFile from the Haskell standard library, which is a direct frontend to the unlink(2) system call in GHC.
chmod
:: [String]Command line arguments
-> IO ()

Execute /bin/chmod

chmod = run "/bin/chmod"
chown
:: [String]Command line arguments
-> IO ()

Execute /bin/chown

chown = run "/bin/chown"
cp
:: Stringsource
-> Stringdestination
-> IO ()
Execute the cp program
mv
:: Stringsource
-> Stringdestination
-> IO ()
Execute the mv program
rename
:: StringOld path
-> StringNew path
-> IO ()
The rename(2) system call to rename and/or move a file. The renameFile action from the Haskell standard library doesn't do it, because the two paths may not refer to directories. Failure results in an IOError thrown. The new path is included in the IOError and can be accessed with IO.ioeGetFileName.
rename_mv
:: FilePathOld path
-> FilePathNew path
-> IO ()

Rename a file. This first tries rename, which is most efficient. If it fails, because source and target path point to different file systems (as indicated by the errno value EXDEV), then /bin/mv is called.

See rename, mv.

force_rename
:: StringOld path
-> StringNew path
-> IO ()

Rename a file or directory, and cope with read only issues.

This renames a file or directory, using rename, sets the necessary write permissions beforehand, and restores them afterwards. This is more efficient than force_mv, because no external program needs to be called, but it can rename files only inside the same file system. See force_cmd for a detailed description.

The new path may be an existing directory. In this case, it is assumed that the old file is to be moved into this directory (like with mv). The new path is then completed with the file name component of the old path. You won't get an "already exists" error.

force_rename = force_cmd rename

See force_cmd, rename.

force_mv
:: StringOld path
-> StringNew path or target directory
-> IO ()

Move a file or directory, and cope with read only issues.

This moves a file or directory, using the external command mv, sets the necessary write permissions beforehand, and restores them afterwards. This is less efficient than force_rename, because the external program mv needs to be called, but it can move files between file systems. See force_cmd for a detailed description.

force_mv src tgt = fill_in_location "force_mv" $ force_cmd (\src tgt -> run "/bin/mv" ["--", src, tgt]) src tgt

See force_cmd, force_mv.

force_rename_mv
:: FilePathOld path
-> FilePathNew path
-> IO ()

Rename a file with rename, or when necessary with mv, and cope with read only issues.

The necessary write permissions are set, then the file is renamed, then the permissions are restored.

First, the rename system call is tried, which is most efficient. If it fails, because source and target path point to different file systems (as indicated by the errno value EXDEV), then /bin/mv is called.

force_rename_mv old new = fill_in_location "force_rename_mv" $ force_cmd rename_mv old new

See rename_mv, rename, mv, force_cmd.

force_cmd
:: (String -> String -> IO ())Command to execute after preparing the permissions
-> StringOld path
-> StringNew path or target directory
-> IO ()

Call a command which moves a file or directory, and cope with read only issues.

This function is for calling a command, which renames files. Beforehand, write permissions are set in order to enable the operation, and afterwards the permissions are restored. The command is meant to be something like rename or run "/bin/mv".

In order to change the name of a file or dirctory, but leave it in the super directory it is in, the super directory must be writeable. In order to move a file or directory to a different super directory, both super directories and the file/directory to be moved must be writeable. I don't know what this behaviour is supposed to be good for.

This function copes with the case that the file/directory to be moved or renamed, or the super directories are read only. It makes the necessary places writeable, calls the command, and makes them read only again, if they were before. The user needs the necessary permissions for changing the corresponding write permissions. If an error occurs (such as file not found, or insufficient permissions), then the write permissions are restored to the state before, before the exception is passed through to the caller.

The command must take two arguments, the old path and the new path. It is expected to create the new path in the file system, such that the correct write permissions of the new path can be set by force_cmd after executing it.

The new path may be an existing directory. In this case, it is assumed that the old file is to be moved into this directory (like with mv). The new path is completed with the file name component of the old path, before it is passed to the command, such that the command is supplied the complete new path.

Examples:

force_cmd rename from to
force_cmd (\from to -> run "/bin/mv" ["-i", "-v", "--", from, to]) from to

See force_rename, force_mv, rename.

force_writeable
:: StringFile or directory to make writeable
-> IO aAction to perform
-> IO aReturns the return value of the action

Make a file or directory writeable for the user, perform an action, and restore its writeable status. An IOError is raised when the user doesn't have permission to make the file or directory writeable.

force_writeable path io = force_writeable2 path (io >>= \res -> return (path, res))

Example:

-- Need to create a new directory in /foo/bar, even if that's write protected
force_writeable "/foo/bar" $ mkdir "/foo/bar/baz"

See force_cmd, force_writeable2.

force_writeable2
:: StringFile or directory to make writeable
-> IO (String, a)Action to perform
-> IO a

Make a file or directory writeable for the user, perform an action, and restore its writeable status. The action may change the name of the file or directory. Therefore it returns the new name, along with another return value, which is passed to the caller.

The writeable status is only changed back if it has been changed by force_writeable2 before. An IOError is raised when the user doesn'h have permission to make the file or directory writeable, or when the new path doesn't exist.

See force_cmd, force_writeable.

getFileStatus'
:: FilePathPath of the file, whose status is to be queried
-> IO FileStatusStatus of the file

This is the System.Posix.Files.getFileStatus function from the GHC libraries, with improved error reporting. The GHC function doesn't include the file name in the IOError when the call fails, making error messages much less useful. getFileStatus' rectifies this.

See getFileStatus.

fileAccess' :: FilePath -> Bool -> Bool -> Bool -> IO Bool

This is the System.Posix.Files.fileAccess function from the GHC libraries, with improved error reporting. The GHC function doesn't include the file name in the IOError when the call fails, making error messages much less useful. fileAccess' rectifies this.

See fileAccess.

setFileMode' :: FilePath -> FileMode -> IO ()

Improved version of System.Posix.Files.setFileMode, which sets the file name in the IOError which is thrown in case of an error. The implementation in GHC 6.2.2 neglects to do this.

setFileMode' path mode =
   fill_in_filename path $
      setFileMode path mode
Interfaces to Some Specific External Commands
mt_status
:: IO (Int, Int)file and block number
Run the command mt status for querying the tape drive status, and parse its output.
fdupes
:: [String]Options for the fdupes program
-> [String]Directories with files to compare
-> IO [[[String]]]For each set of identical files, and each of the specified directories, the paths of the identical files in this directory.

Call the fdupes program in order to find identical files. It outputs a list of groups of file names, such that the files in each group are identical. Each of these groups is further analysed by the fdupes action. It is split to a list of lists of paths, such that each list of paths corresponds to one of the directories which have been searched by the fdupes program. If you just want groups of identical files, then apply map concat to the result.

The fdupes program doesn't handle multiple occurences of the same directory, or in recursive mode one specified directory containing another, properly. The same file may get reported multiple times, and identical files may not get reported.

The paths are normalised (using normalise_path).

du
:: (Integral int, Read int)
=> intblock size, this is the --block-size option.
-> Stringpath of the file or directory to determine the size of
-> IO intsize in blocks
Call the du program. See du(1).
Calling External Programs
Running a Subroutine in a Separate Process

It can by very useful to fork a child process, which executes a subroutine of the main program. In the following example, paths are piped to the recode program in order to convert them from ISO 8859-1 to UTF-8. Its output is read by a subroutine of the main program, which can use it to rename the files.

main = mainwrapper $ do
   paths <- contents "-"
   pipe_to paths $ 
           (     execp "recode" ["-f", "latin1..utf8"]
             -|= (do paths_utf8 <- lazy_contents "-"
                     mapM_ (\(path, path_utf8) -> 
                               ...
                           )
                           (zip (lines paths) (lines paths_utf8))
                 )
           )

The same could be achieved this way:

main = mainwrapper $ do
   paths <- contents "-"
   paths_utf8 <- 
      pipe_from (     putStr paths
                  -|= execp "recode" ["-f", "latin1..utf8"]
                )
   mapM_ (\(path, path_utf8) -> 
             ...
         )
         (zip (lines paths) (lines paths_utf8))

Most of the time, it's intuitive. But sometimes, the forked subroutine interferes with the parent process.

When the process clones itself by calling fork(2), everything gets duplicated - open files, database connections, window system connections... This becomes an issue when the child process uses any of it. For instance, any buffered, not yet written data associated with a file handle gets duplicated. When the child process uses that handle, that data gets written twice.

The functions which fork a child process (call, spawn, silently, pipe_to etc.) flush stdout and stderr (should be unbuffered) before the fork. So the child process can use them. The pipe functions also take care of stdin, which is used to read from the pipe. But they don't know about any other handles.

What happens when the subroutine finishes? The control flow would escape into the main program, doing unexpected things. Therefore the functions which fork an IO action terminate the child process when the subroutine finishes. They do so by calling _exit, circumventing normal program shutdown. Normal shutdown would flush cloned file handles, shut down database connections now shared with the parent process etc. Only the stdout and stderr are flushed before. If the child process requires any more cleanup on termination, such as flushing new file handles created in the child process, it's the responsibility of the programmer to do so before the subroutine exits.

When the subroutine throws an exception, the control flow isn't allowed to escape into the main program either. Any exception is caught, an error message is printed, and the child process is terminated with _exit 1.

The subroutine must not terminate the child process normally, by calling exitWith or exitFailure. It should terminate with _exit. Don't forget to flush stdout before, which won't be line buffered when not connected to a terminal. It can also just leave the subroutine. The functions which fork child processes intercept any attempt of normal program shutdown in the child process (it's an ExitException, see the GHC library documentation). A warning message is printed, and the child is terminated with _exit, with the same exit code which it would have been.

About the exec Functions

There are five exec variants: exec, execp, exece, execpe and execute_file. The first four are frontends to execute_file. They differ in whether the PATH is searched, and in whether a new environment is installed. The latter is a replacement for System.Posix.Process.executeFile. They are designed to work intuitively in conjunction with the functions which fork a child process, such as run, call, spawn, pipe_to etc.

Before replacing the process, stdout and stderr are flushed, so no yet unwritten data is lost. Then the file descriptors of the process are prepared for the exec, such that everything works as expected. The standard file descriptors 0-2 are made to correspond to the standard handles again (this might have changed, see HsShellScript#exec). They are also reset to blocking mode. All others are closed when the exec succeeds.

You can't use executeFile directly, unless you take care of the things outlined at HsShellScript#exec and execute_file by yourself.

If replacing the process fails (for instance, because the program wasn't found), then everything is restored to original state, and an IOError is thrown, and the process continues with normal error handling. Normally, the exec functions are used in conjunction with some of the functions which fork a child process. They also handle errors, so the forked action doesn't need to cope with failure of exec. The error handling and termination is done via the child function.

Sometimes you want to pass an open file descriptor to the program. In this case, you can't use the exec variants. You need to call executeFile directly, and take care of the outlined matters by yourself. In this case, take a look at the source code of execute_file.

For full details, see the documentation of execute_file.

Functions for Calling External Programs
call
:: IO aaction to execute as a child process
-> IO ()

Execute an IO action as a separate process, and wait for it to finish. Report errors as exceptions.

The program forks a child process and performs the specified action. Then it waits for the child process to finish. If it exits in any way which indicates an error, the ProcessStatus is thrown as a dynamic exception.

The parent process waits for the child process, if it has been stopped with the STOP signal.

See HsShellScript#subr for further details.

See spawn.

spawn
:: IO aAction to execute as a child process.
-> IO ProcessIDProcess ID of the new process.

Execute an IO action as a separate process, and continue without waiting for it to finish.

The program forks a child process, which performs the specified action and terminates. The child's process ID is returned.

See HsShellScript#subr for further details.

See call.

run
:: FilePathName of the executable to run
-> [String]Command line arguments
-> IO ()

Run an external program. This starts an external program as a child process, and waits for it to finish. The executable is searched via the PATH.

When the specified program can't be executed, an error message is printed, and the main process gets a ProcessStatus thrown as a dynamic exception, with the value Exited (ExitFailure 1). This means that the main program can't distinguish between failure of calling the program and the program exiting with an exit code of 1.

run prog par is essentially call (execp prog par).

Example:

run "/usr/bin/foobar" ["some", "args"]
   `catchDyn` (\ps -> do -- oops...
              )

See call, spawn.

exec
:: StringFull path to the executable
-> [String]Command line arguments
-> IO aNever returns

Execute an external program. This replaces the running process. The path isn't searched, the environment isn't changed. In case of failure, an IOError is thrown.

exec path args =
   execute_file path False args Nothing

See execute_file, HsShellScript#exec.

execp
:: StringName or path of the executable
-> [String]Command line arguments
-> IO aNever returns

Execute an external program. This replaces the running process. The path is searched, the environment isn't changed. In case of failure, an IOError is thrown.

execp prog args =
   execute_file prog True args Nothing

See execute_file, HsShellScript#exec.

exece
:: StringFull path to the executable
-> [String]Command line arguments
-> [(String, String)]New environment
-> IO aNever returns

Execute an external program. This replaces the running process. The path isn't searched, the environment of the program is set as specified. In case of failure, an IOError is thrown.

exece path args env =
   execute_file path False args (Just env)

See execute_file, HsShellScript#exec.

execpe
:: StringName or path of the executable
-> [String]Command line arguments
-> [(String, String)]New environment
-> IO aNever returns

Execute an external program. This replaces the running process. The path is searched, the environment of the program is set as specified. In case of failure, an IOError is thrown.

execpe prog args env =
   execute_file prog True args (Just env)

See execute_file, HsShellScript#exec.

echo
:: (FilePath -> [String] -> IO ())Action to perform
-> FilePathName or path of the executable to run
-> [String]Command line arguments
-> IO ()

Print an action as a shell command, then perform it.

This is used with actions such as run, exec or call. For instance, echo run prog args is a variant of run prog args, which prints what is being done before doing it.

See run, call, exec.

silently
:: IORef StringA mutable variable, which gets the output (stdout and stderr) of the action appended.
-> IO ()The IO action to run.
-> IO ()

Run a subroutine as a child process, but don't let it produce any messages. Read its stdout and stderr instead, and append it to the contents of a mutable variable. The idea is that you can run some commands silently, and report them and their messages to the user only when something goes wrong.

If the child process terminates in a way which indicates an error, then the process status is thrown, in the same way as run does. If the subroutine throws an (Exited ec) exception (of type ProcessStatus), such as thrown by run, then the child process exits with the same exit code, such that the parent process reports it to the caller, again as a ProcessStatus exception.

When the subroutine finishes, the child process is terminated with _exit 0. When it throws an exception, an error message is printed and it is terminated with _exit 1. See HsShellScript#subr for details.

The standard output (and the standard error output) of the parent process are flushed before the fork, such that no output appears twice.

Example:

let handler :: IORef String -> ProcessStatus -> IO ()
    handler msgref ps = do hPutStrLn stderr ("Command failed with " ++ show ps ++ ". Actions so far: ")
                           msg <- readIORef msgref
                           hPutStrLn stderr msg
                           exitWith (ExitFailure 1)

msgref <- newIORef ""
do silently msgref $ do putStrLn "Now doing foobar:"
                        echo exec "/foo/bar" ["arguments"]
   silently msgref $ echo exec "/bar/baz" ["arguments"]
`catchDyn` (handler msgref)

See lazy_pipe_from, call, run, GHC.IOBase.IORef.

system_throw :: String -> IO ()

Call the shell to execute a command. In case of an error, throw the ProcessStatus (such as (Exited (ExitFailure ec))) as a dynamic exception. This is like the Haskell standard library function system, except that error handling is brought in accordance with HsShellScript's scheme.

exitcode . system_throw is the same as the system function, except that when the called shell is terminated or stopped by a signal, this still leads to the ProcessStatus thrown as a dynamic exception. The Haskell library report says nothing about what happens in this case, when using the system function.

system_throw cmd = run "/bin/sh" ["-c", "--", cmd]
execute_file
:: FilePathProgram to call
-> BoolSearch PATH?
-> [String]Arguments
-> Maybe [(String, String)]Optionally new environment
-> IO aNever returns

This is a replacement for System.Posix.Process.executeFile. It does additional preparations, then calls executeFile. executeFile can't normally be used directly, because it doesn't do the things which are outlined here.

This are the differences to executeFile:

1. stdout and stderr are flushed.

2. The standard file descriptors 0-2 are made copies of the file descriptors which the standard handles currently use. This is necessary because they might no longer use the standard handles. See HsShellScript#fdpipes.

If the standard handles stdin, stdout, stderr aren't in closed state, and they aren't already connected to the respective standard file descriptors, their file descriptors are copied to the respective standard file descriptors (with dup2). Backup copies are made of the file descriptors which are overwritten. If some of the standard handles are closed, the corresponding standard file descriptors are closed as well.

3. All file descriptors, except for the standard ones, are set to close on exec (see fcntl(2)), and will be closed on successful replacement of the process. Before that, the old file descriptor flags are saved.

4. The standard file descriptors are set to blocking mode, since GHC 6.2.2 sets file descriptors to non-blocking (except 0-2, which may get overwritten by a non-blocking one in step 2). The called program doesn't expect that.

5. In case replacing the process fails, the file descriptors are reset to the original state. The file descriptors flags are restored, and the file descriptors 0-2 are overwritten again, with their backup copies. Then an IOError is thrown.

6. In any IOError, the program is filled in as the file name (executeFile neglects this).

7. The return type is a generic a, rather than ().

Also see HsShellScript#exec.

child :: IO a -> IO b

Modify a subroutine action in order to make it suitable to run as a child process.

This is used by call, silently, pipe_to, exec etc. The action is executed. When it returns, the (child) process is terminated with _exit 0 (after flushing stdout), circumventing normal program shutdown. When it throws an exception, an error message is printed and the (child) process is terminated with _exit 1.

Redirecting Input and Output
(->-)
:: IO aAction, whose output will be redirected
-> FilePathFile to redirect the output to
-> IO aResult action

Redirect the standard output of the specified IO action to a file. The file will be overwritten, if it already exists.

What's actually modified is the stdout handle, not the file descriptor 1. The exec functions know about this. See HsShellScript#fdpipes and HsShellScript#exec for details.

Example:

call (execp "foo" [] ->- "bar")

See call, run, ->>-, =>-.

(->>-)
:: IO aAction, whose output will be redirected
-> FilePathFile to redirect the output to
-> IO aResult action

Redirect the standard output of the specified IO action to a file. If the file already exists, the output will be appended.

What's actually modified is the stdout handle, not the file descriptor 1. The exec functions know about this. See HsShellScript#fdpipes and HsShellScript#exec for details.

Example:

call (exec "/path/to/foo" [] ->>- "bar")

See call, run, '(->-)', '(=>>-)'.

(=>-)
:: IO aAction, whose error output will be redirected
-> FilePathFile to redirect the error output to
-> IO aResult action

Redirect the standard error output of the specified IO action to a file. If the file already exists, it will be overwritten.

What's actually modified is the stderr handle, not the file descriptor 2. The exec functions know about this. See HsShellScript#fdpipes and HsShellScript#exec for details.

Example: call (exec "/path/to/foo" [] =>- "/dev/null")

See call, run, '(->-)', '(=>>-)'.

(=>>-)
:: IO aAction, whose error output will be redirected
-> FilePathFile to redirect the error output to
-> IO aResult action

Redirect the standard error output of the specified IO action to a file. If the file already exists, the output will be appended.

What's actually modified is the stderr handle, not the file descriptor 2. The exec functions know about this. See HsShellScript#fdpipes and HsShellScript#exec for details.

Example: call (exec "/path/to/foo" [] =>>- "log")

See call, run, '(->>-)', '(=>-)'.

(-<-) :: IO a -> FilePath -> IO a

Redirect stdin from a file. This modifies the specified action, such that the standard input is read from a file.

What's actually modified is the stdin handle, not the file descriptor 0. The exec functions know about this. See HsShellScript#fdpipes and HsShellScript#exec for details.

Example:

call (exec "/path/to/foo" [] -<- "bar")

See exec, run, '(->-)', '(=>-)'.

(-&>-)
:: IO aAction, whose output and error output will be redirected
-> FilePathFile to redirect to
-> IO aResult action

Redirect both stdout and stderr to a file. This is equivalent to the shell's &> operator. If the file already exists, it will be overwritten.

What's actually modified are the stdout and stderr handles, not the file descriptors 1 and 2. The exec functions know about this. See HsShellScript#fdpipes and HsShellScript#exec for details.

(-&>-) io path = err_to_out io ->- path

Example:

call (exec "/path/to/foo" [] -&>- "log")

See '(-&>>-)', err_to_out.

(-&>>-)
:: IO aAction, whose output and error output will be redirected
-> FilePathFile to redirect to
-> IO aResult action

Redirect both stdout and stderr to a file. If the file already exists, the output will be appended.

What's actually modified are the stdout and stderr handles, not the file descriptors 1 and 2. The exec functions know about this. See HsShellScript#fdpipes and HsShellScript#exec for details.

Example:

call (exec "/path/to/foo" [] -&>>- "log")
(-&>>-) io path = (err_to_out >> io) (->>-) path

See '(-&>-)', out_to_err.

err_to_out :: IO a -> IO a

Send the error output of the specified action to its standard output.

What's actually modified is the stdout handle, not the file descriptor 1. The exec functions know about this. See HsShellScript#fdpipes and HsShellScript#exec for details.

out_to_err :: IO a -> IO a

Send the output of the specified action to its standard error output.

What's actually modified is the stderr handle, not the file descriptor 2. The exec functions know about this. See HsShellScript#fdpipes and HsShellScript#exec for details.

Pipes
File Descriptors in Pipes

With HsShellScript, you build pipes from IO actions, which can replace themselves with an external program via a variant of exec. It's mostly transparent whether some part of the pipe is a subroutine of the main program, or an external program.

But actually, there are two cases. When the forked process is a subroutine, the child's stdin handle is connected to the parent. On the other hand, when the forked process consists of calling an exec variant, that program's file descriptor 0 is to be connected to the parent process.

Normally, stdin connects exactly to file descriptor 0, but this isn't necessarily the case. For instance, when stdin has been closed, the file descriptor will be reused on the next occasion. When it is reopened again by calling GHC.Handle.hDuplicateTo h stdin, then the new stdin will be using a different file descriptor, and file descriptor 0 will be in use by another handle. Thus, when forking a subroutine, we're connected via stdin, but we can't expect to be connected via file descriptor 0.

Actually, we'll never be, because of the way GHC.Handle.hDuplicateTo is implemented in GHC 6.2.2. It makes a copy of the file descriptor which is duplicated, and makes the target handle use that, rather than overwrite the target handle's file descriptor using dup2. (This will change in the next GHC version). HsShellScript's pipe creation functions create a new pipe, fork the child process, and call hDuplicateTo to replace the standard handle. This way we always end up with a standard handle not using the corresponding standard file descriptor.

In case the child process is to be replaced with another program, we need to make sure that right file descriptor connects to the parent process. This is accomplished by the exec functions. They replace the standard file descriptors with the ones that the standard handles currently use. See HsShellScript#exec for details.

These two examples work as expected.

Example 1:

-- This closes stdin.
c <- contents "-"

pipe_to something
   (     -- execp arranges for "something" to go to foo's file descriptor 0
         execp "foo" []

     -|- (do -- Read foo's standard output from new stdin handle
             c' <- lazy_contents "-"
             ...
         )
   )

Example 2:

-- Call wc to count the number of lines in txt
count <- fmap (read . chomp) $
              pipe_from (putStr txt -|= execp "wc" ["-l"])
Pipe Creation Functions
(-|-)
:: IO aAction which won't be forked
-> IO bAction which will be forked and connected with a pipe
-> IO aResult action

Build left handed pipe of stdout.

"p -|- q" builds an IO action from the two IO actions p and q. q is executed in an external process. The standard output of p is sent to the standard input of q through a pipe. The result action consists of forking off q (connected with a pipe), and p.

The result action does not run p in a separate process. So, the pipe itself can be seen as a modified action p, forking a connected q. Normally, the pipe itself will be forked, too. The pipe is called "left handed", because p has this property, and not q.

The exit code of q is silently ignored. The process ID of the forked copy of q isn't returned to the caller, so it's lost.

See HsShellScript#subr and HsShellScript#exec for further details.

Examples:

call (exec "/usr/bin/foo" [] -|- exec "/usr/bin/bar" [])
call (    execp "foo" ["..."]
      -|= ( -- Do something with foo's output
            do cnt <- lazy_contents "-"
               ...
          )
     )

See call, '(=|-)', '(-|=)'.

(=|-)
:: IO aAction which won't be forked
-> IO bAction which will be forked and connected with a pipe
-> IO aResult action

Build left handed pipe of stderr.

"p =|- q" builds an IO action from the two IO actions p and q. q is executed in an external process. The standard error output of p is sent to the standard input of q through a pipe. The result action consists of forking off q (connected with a pipe), and p.

The result action does not run p in a separate process. So, the pipe itself can be seen as a modified action p, forking a connected q. Normally, the pipe itself will be forked, too. The pipe is called "left handed", because p has this property, and not q.

The exit code of q is silently ignored. The process ID of the forked copy of q isn't returned to the caller, so it's lost.

See HsShellScript#subr and HsShellScript#exec for further details.

Example:

call (exec "/usr/bin/foo" [] =|- exec "/usr/bin/bar" [])

See call, '(-|-)', '(-|=)'.

(-|=)
:: IO aAction which will be forked and connected with a pipe
-> IO bAction which won't be forked
-> IO bResult action

Build right handed pipe of stdout.

"p -|= q" builds an IO action from the two IO actions p and q. p is executed in an external process. The standard output of p is sent to the standard input of q through a pipe. The result action consists of forking off p (connected with a pipe), and q.

The result action does not run q in a separate process. So, the pipe itself can be seen as a modified action q, forking a connected p. Normally, the pipe itself will be forked, too. The pipe is called "right handed", because q has this property, and not p.

The exit code of p is silently ignored. The process ID of the forked copy of q isn't returned to the caller, so it's lost.

See HsShellScript#subr and HsShellScript#exec for further details.

Example:

@call (exec \"\/usr\/bin\/foo\" [] -|= exec \"\/usr\/bin\/bar\" [])@

See call, '(=|-)', '(=|=)'.

(=|=)
:: IO aAction which will be forked and connected with a pipe
-> IO bAction which won't be forked
-> IO bResult action

Build right handed pipe of stderr.

"p =|= q" builds an IO action from the two IO actions p and q. p is executed in an external process. The standard error output of p is sent to the standard input of q through a pipe. The result action consists of forking off p (connected with a pipe), and q.

The result action does not run q in a separate process. So, the pipe itself can be seen as a modified action q, forking a connected p. Normally, the pipe itself will be forked, too. The pipe is called "right handed", because q has this property, and not p.

The exit code of p is silently ignored. The process ID of the forked copy of q isn't returned to the caller, so it's lost.

See HsShellScript#subr and HsShellScript#exec for further details.

Example:

 call (exec "/usr/bin/foo" [] =|= exec "/usr/bin/bar" [])

See call, =|-, -|=.

pipe_to
:: StringText to pipe
-> IO aAction to run as a separate process, and to pipe to
-> IO ()

Run an IO action as a separate process, and pipe some text to its stdin. Then close the pipe and wait for the child process to finish. If it exits in a way which indicates an error, the ProcessStatus is thrown as a dynamic exception.

Example: pipe_to "blah" $ exec "/usr/bin/foo" ["bar"]

See call, run, -<-, h_pipe_to. See HsShellScript#fdpipes for more details.

h_pipe_to
:: IO aAction to run as a separate process, and to pipe to
-> IO (Handle, ProcessID)Returns handle connected to the standard input of the child process, and the child's process ID

Run an IO action as a separate process, and connect to its stdin with a pipe.

Example: h <- h_pipe_to $ exec "/usr/bin/foo" ["bar"]

See -<-, pipe_to, pipe_from, pipe_from2. See HsShellScript#fdpipes for more details.

pipe_from
:: IO aAction to run as a separate process
-> IO StringThe called program's standard output

Run an IO action as a separate process, and read its stdout strictly. Then wait for the child process to finish. This is like the backquote feature of shells.

If the child process exits with a non-zero exit code, the ProcessStatus is thrown as a dynamic exception.

The whole output is returned, no trailing newline character is removed, like the shell does with backquotes. You may want to apply chomp to the result.

Example:

output <- pipe_from $ exec "/bin/foo" ["bar"]

See exec, pipe_to, pipe_from2, h_pipe_from, lazy_pipe_from, chomp, silently. See HsShellScript#fdpipes for more details.

lazy_pipe_from
:: IO aAction to run as a separate process
-> IO (String, ProcessID)The action's lazy output and the process ID of the child process

Run an IO action as a separate process, and read its stdout, This is like the backquote feature of shells. The output is read lazily, as the returned string is evaluated.

The child's output along with its process ID are returned. The process ID can be used with System.Posix.getProcessStatus to get the child process' exit code. Be aware that you must evaluate the whole string, before calling getProcessStatus blockingly, or you'll get a deadlock.

The whole output is returned, no trailing newline character is removed, like the shell does with backquotes. You'll possibly want to apply chomp to the result.

Example:

(txt, pid) <- lazy_pipe_from $ exec "/usr/bin/foo" ["bar"]
...
-- Done, but must read the rest of the output
seq (length txt) (return ())
(Just ps) <- getProcessStatus True False pid

See exec, pipe_to, pipe_from, h_pipe_from, lazy_pipe_from2, silently. See HsShellScript#fdpipes for more details.

h_pipe_from
:: IO aAction to run as a separate process, and to pipe from
-> IO (Handle, ProcessID)Returns handle connected to the standard output of the child process, and the child's process ID

Run an IO action as a separate process, and connect to its stdout with a pipe.

A handle connected to the child process, and the process ID of the child are returned. The process ID can be used with System.Posix.getProcessStatus to get the child's exit code. You must either ensure that all data has been read, or close the handle, before calling getProcessStatus blockingly. Otherwise you'll get a deadlock. When you close the handle before all data has been read, then the child gets a SIGPIPE signal.

Example:

h <- h_pipe_from $ exec "/usr/bin/foo" ["bar"]

See exec, pipe_to, h_pipe_from2, pipe_from, lazy_pipe_from, chomp, silently. See HsShellScript#fdpipes for more details.

pipe_from2
:: IO aAction to run as a separate process
-> IO (String, ProcessStatus)The called program's standard output

Run an IO action as a separate process, and read its stderr strictly. Then wait for the child process to finish, and return the text along with its exit code.

Example:

(errmsg, ec) <- pipe_from2 $ exec "/bin/foo" ["bar"] ->- "/dev/null"

when (ec /= Exited ExitSuccess) $ do
   errm errmsg
   ...

See exec, pipe_to, pipe_from, h_pipe_from2, lazy_pipe_from2, silently. See HsShellScript#fdpipes for more details.

lazy_pipe_from2
:: IO aAction to run as a separate process
-> IO (String, ProcessID)The action's lazy output and the process ID of the child process

Run an IO action as a separate process, and read its stderr. The output is read lazily, as the returned string is evaluated.

The child's error output along with its process ID are returned. The process ID can be used with System.Posix.getProcessStatus to get the child process' exit code. Be aware that you must evaluate the whole string, before calling getProcessStatus blockingly, or you'll get a deadlock.

Example:

(errmsg, pid) <- lazy_pipe_from2 $ exec "/usr/bin/foo" ["bar"] ->- "/dev/null"
...
-- Read enough error messages, terminate the child.
signalProcess killProcess pid

-- Make sure the file descriptor gets closed, or you may run out of file descriptors.
seq (length errmsg) (return ())

See exec, pipe_to, pipe_from2, h_pipe_from2, lazy_pipe_from, silently. See HsShellScript#fdpipes for more details.

h_pipe_from2
:: IO aAction to run as a separate process, and to pipe from
-> IO (Handle, ProcessID)Returns handle connected to the standard output of the child process, and the child's process ID

Run an IO action as a separate process, and connect to its stderr with a pipe.

A handle connected to the child process' standard error output, and the process ID of the child are returned. The process ID can be used with System.Posix.getProcessStatus to get the child's exit code. You must either ensure that all data has been read, or close the handle, before calling getProcessStatus blockingly. Otherwise you'll get a deadlock. When you close the handle before all data has been read, then the child gets a SIGPIPE signal. Of course, you can also use the process ID to kill the child process.

Example:

h <- h_pipe_from2 $ exec "/usr/bin/foo" ["bar"]

See exec, pipe_to, h_pipe_from, pipe_from2, lazy_pipe_from2, chomp, silently. See HsShellScript#fdpipes for more details.

pipes
:: IO aAction to run in a new process
-> BoolWhether to make stdin pipe
-> BoolWhether to make stdout pipe
-> BoolWhether to make stderr pipe
-> IO (Maybe Handle, Maybe Handle, Maybe Handle, ProcessID)Pipes to the new process's stdin, stdout and stderr, if applicable; and its process id.

Run an IO action as a separate process, and optionally connect to its stdin, its stdout and its stderr output with pipes.

See pipe_from, pipe_from2, pipe_to.

Shell-like Quoting
shell_command
:: Stringname or path of the executable
-> [String]command line arguments
-> Stringshell command

Generate command (for a shell) which corresponds to the specified program name and argument list. The program name and arguments are the usual parameters for calling an external program, like when using runProcess or run. The generated shell command would achieve the same effect. The name and the arguments are properly quoted, using shell_quote.

Note: The quoted strings are correctly recognized in shell scripts. But the shell bash has an annoying history expansion "feature", which causes it to choke on exclamation marks, when in interactive mode, even when quoted with double quotes. You can turn it off with set +o histexpand.

shell_quote :: String -> String

Quote shell metacharacters.

This function quotes strings, such that they are not misinterpreted by the shell. It tries to be friendly to a human reader - when special characters are present, then the string is quoted with double quotes. If not, it is left unchanged.

The list of exacly which characters need to be quoted has been taken from the bash source code. Bash in turn, implements POSIX 1003.2. So the result produced should be correct. From the bash info pages: "... the rules for evaluation and quoting are taken from the POSIX 1003.2 specification for the standard Unix shell."

Note: The quoted strings are correctly recognized in shell scripts. But the shell bash has an annoying history expansion "feature", which causes it to choke on exclamation marks, when in interactive mode, even when quoted with double quotes. You can turn it off with set +o histexpand.

See quote.

quote0 :: String -> String

Quote special characters inside a string for the shell

This quotes special characters inside a string, such that it is recognized as one string by the shell when enclosed in double quotes. Doesn't add the double quotes.

Note: The quoted strings are correctly recognized in shell scripts. But the shell bash has an annoying history expansion "feature", which causes it to choke on exclamation marks, when in interactive mode, even when quoted with double quotes. You can turn it off with set +o histexpand.

See quote, shell_quote.

quote :: String -> String

Quote a string for the shell

This encloses a string in double quotes and quotes any special characters inside, such that it will be recognized as one string by a shell. The double quotes are added even when they aren't needed for this purpose.

Note: The quoted strings are correctly recognized in shell scripts. But the shell bash has an annoying history expansion "feature", which causes it to choke on exclamation marks, when in interactive mode, even when quoted with double quotes. You can turn it off with set +o histexpand.

See quote0, shell_quote.

Creating temporary files and directories
tmp_file
:: StringPrefix for the path to generate.
-> IO FilePathPath of the created file.

Create a temporary file. This will create a new, empty file, with read-write permissions for the user, and no permissons for the group and others. The path consists of the specified prefix, a dot, and six random characters (digits and letters).

tmp_file prefix = temp_file 6 (prefix ++ ".") ""

See temp_file, tmp_dir, with_tmp_file.

tmp_dir
:: StringPrefix for the path to generate.
-> IO FilePathPath of the created directory.

Create a temporary directory. This will create a new directory, with read-write-execute permissions for the user (unless further restricted by the process's umask), and no permissons for the group and others. The path consists of the specified prefix, a dot, and six random characters (digits and letters).

tmp_dir prefix = temp_dir 6 (prefix ++ ".") ""

See temp_dir, tmp_file, with_tmp_dir.

temp_file
:: IntNumber of random characters to intersperse. Must be large enough, such that most combinations can't already exist.
-> StringPrefix for the path to generate.
-> StringSuffix for the path to generate.
-> IO FilePathPath of the created file.

Create a temporary file. This will create a new, empty file, with a path which did not previously exist in the file system. The path consists of the specified prefix, a sequence of random characters (digits and letters), and the specified suffix. The file is created with read-write permissions for the user, and no permissons for the group and others. The ownership is set to the effective user ID of the process. The group ownership is set either to the effective group ID of the process or to the group ID of the parent directory (depending on filesystem type and mount options on Linux - see open(2) for details).

See tmp_file, temp_dir, with_temp_file.

temp_dir
:: IntNumber of random characters to intersperse. Must be large enough, such that most combinations can't already exist.
-> StringPrefix for the path to generate.
-> StringSuffix for the path to generate.
-> IO FilePathGenerated path.

Create a temporary directory. This will create a new directory, with a path which did not previously exist in the file system. The path consists of the specified prefix, a sequence of random characters (digits and letters), and the specified suffix. The directory is normally created with read-write-execute permissions for the user, and no permissons for the group and others. But this may be further restricted by the process's umask in the usual way.

The newly created directory will be owned by the effective uid of the process. If the directory containing the it has the set group id bit set, or if the filesystem is mounted with BSD group semantics, the new directory will inherit the group ownership from its parent; otherwise it will be owned by the effective gid of the process. (See mkdir(2))

See tmp_dir, temp_file, with_temp_dir.

temp_path
:: IntNumber of random characters to intersperse. Must be large enough, such that most combinations can't already exist.
-> StringPrefix for the path to generate.
-> StringSuffix for the path to generate.
-> IO FilePathGenerated path.

Create a temporary path. This will generate a path which does not yet exist in the file system. It consists of the specified prefix, a sequence of random characters (digits and letters), and the specified suffix.

Avoid relying on the generated path not to exist in the file system. Or else you'll get a potential race condition, since some other process might create the path after temp_path, before you use it. This is a security risk. The global random number generator (Random.randomRIO) is used to generate the random characters. These might not be that random after all, and could potentially be guessed. Rather use temp_file or temp_dir.

See temp_file, temp_dir.

with_tmp_file
:: StringPrefix for the path to generate.
-> (Handle -> IO a)Action to perform.
-> IO aReturns the value returned by the action.

Create and open a temporary file, perform some action with it, and delete it afterwards. This is a front end to the tmp_file function. The file and its path are created in the same way. The IO action is passed a handle of the new file. When it finishes - normally or with an exception - the file is deleted.

This works with all kinds of exceptions (ordinary or dynamic).

See tmp_file, with_temp_file, with_tmp_dir.

with_tmp_dir
:: StringPrefix for the path to generate.
-> (FilePath -> IO a)Action to perform.
-> IO aReturns the value returned by the action.

Create a temporary directory, perform some action with it, and delete it afterwards. This is a front end to the tmp_dir function. The directory and its path are created in the same way. The IO action is passed the path of the new directory. When it finishes - normally or with an exception - the directory is deleted.

The action must clean up any files it creates inside the directory by itself. with_tmp_dir doesn't delete any files inside, so the directory could be deleted. If the directory isn't empty, an IOError results (thrown by Directory.removeDirectory, with the path filled in).

This works with all kinds of exceptions (ordinary or dynamic).

See tmp_dir, with_temp_dir, with_tmp_file.

with_temp_file
:: IntNumber of random characters to intersperse. Must be large enough, such that most combinations can't already exist.
-> StringPrefix for the path to generate.
-> StringSuffix for the path to generate.
-> (Handle -> IO a)Action to perform.
-> IO aReturns the value returned by the action.

Create and open a temporary file, perform some action with it, and delete it afterwards. This is a front end to the temp_file function. The file and its path are created in the same way. The IO action is passed a handle of the new file. When it finishes - normally or with an exception - the file is deleted.

This works with all kinds of exceptions (ordinary or dynamic).

See temp_file, with_tmp_file, with_temp_dir.

with_temp_dir
:: IntNumber of random characters to intersperse. Must be large enough, such that most combinations can't already exist.
-> StringPrefix for the path to generate.
-> StringSuffix for the path to generate.
-> (FilePath -> IO a)Action to perform.
-> IO aReturns the value returned by the action.

Create a temporary directory, perform some action with it, and delete it afterwards. This is a front end to the temp_dir function. The directory and its path are created in the same way. The IO action is passed the path of the new directory. When it finishes - normally or with an exception - the directory is deleted.

The action must clean up any files it creates inside the directory by itself. with_temp_dir doesn't delete any files inside, so the directory could be deleted. If the directory isn't empty, an IOError results (thrown by Directory.removeDirectory, with the path filled in).

This works with all kinds of exceptions (ordinary or dynamic).

See temp_dir, with_tmp_dir, with_temp_file.

Reading mount information
data Mntent

One entry of mount information. This is the same as struct mntent from <mntent.h>. A list of these is returned by the functions which read mount information.

This derives Read, Show, Typeable, Eq.

See read_mounts, read_mtab, read_fstab.

Constructors
Mntent
mnt_fsname :: StringDevice file ("name of mounted file system")
mnt_dir :: StringMount point
mnt_type :: StringWhich kind of file system ("see mntent.h")
mnt_opts :: StringMount options ("see mntent.h")
mnt_freq :: IntDump frequency in days
mnt_passno :: Int"Pass number on parallel fsck"
Instances
Read Mntent
Show Mntent
Typeable Mntent
Eq Mntent
read_mounts
:: StringFile to read (typically /etc/mtab or /etc/fstab)
-> IO [Mntent]Mount information in that file

Read mount information. This is a front end to the setmntent(3), getmntent(3), endmntent(3) system library functions.

When the setmntent call fails, the errno value is converted to an IOError and thrown.

See read_mtab, read_fstab.

read_mtab :: IO [Mntent]

Get the currently mounted file systems.

read_mtab = read_mounts "/etc/mtab"

See read_mounts.

read_fstab :: IO [Mntent]

Get the system wide file system table.

read_fstab = read_mounts "/etc/fstab"

See read_mounts.

Colorful logging and error reporting
logm
:: StringMessage to print
-> IO ()

Colorful log message to stderr.

This prints a message to stderr. When stderr is connected to a terminal (as determined by isatty(3)), additional escape sequences are printed, which make the message appear in cyan.

Additionally, a newline character is output at the end.

See logm_, errm, errm_.

logm_ :: String -> IO ()

Colorful log message to stderr.

This prints a message to stderr. When stderr is connected to a terminal (as determined by isatty(3)), additional escape sequences are printed, which make the message appear in cyan.

No a newline character is output at the end.

See logm, errm, errm_.

errm :: String -> IO ()

Colorful error message to stderr.

This prints a message to stderr. When stderr is connected to a terminal (as determined by isatty(3)), additional escape sequences are printed, which make the message appear in red.

Additionally, a newline character is output at the end.

See logm, logm_, errm_.

errm_ :: String -> IO ()

Colorful error message to stderr.

This prints a message to stderr. When stderr is connected to a terminal (as determined by isatty(3)), additional escape sequences are printed, which make the message appear in red.

No a newline character is output at the end.

See logm, logm_, errm.

Miscellaneous
zeros
:: Inthow many characters to fill up
-> Intvalue to represent as a string
-> Stringstring representation of the value, using the specified number of characters
Format an Int with leading zeros.
chomp
:: StringString to be chomped
-> StringSame string, except for no newline characters at the end
Remove trailing newlines. This is silimar to perl's chomp procedure.
lazy_contents
:: StringEither the name of a file, or "-"
-> IO StringThe lazily read contents of the file or stdin.

Get contents of a file or of stdin. This is a simple frontend to hGetContents. A file name of "-" designates stdin. The contents are read lazily as the string is evaluated.

(The handle which we read from will be in semi-closed state. Once all input has read, it is closed automatically (Haskell Library Report 11.2.1). Therefore we don't need to return it).

lazy_contents path = do
    h   <- if path == "-" then return stdin else openFile path ReadMode
    hGetContents h
contents
:: Stringeither the name of a file, or "-" for stdin
-> IO Stringthe contents of the file or of standard input
Get contents of a file or of stdin eagerly. This is the same as lazy_contents, except for the contents being read immediately.
Error Handling
mainwrapper
:: IO aShould be main
-> IO aWrapped main

Error reporting wrapper for the main function. This catches any HsShellScript generated exceptions, and IOErrors, prints an error message and exits with exitFailure. The main function typically looks like this:

main = mainwrapper $ do ...'

The exceptions caught are ArgError, ProcessStatus and IOError.

errno
:: IO Errnoerrno value
Read the global system error number. This is the POSIX errno value. This function is redundant. Use Foreign.C.Error.getErrno instead.
strerror
:: Errnoerrno value
-> IO StringCorresponding error message

Generate an error message from an errno value. This is the POSIX strerror system library function.

See the man page strerror(3).

perror'
:: Errnoerrno error number
-> StringText to precede the message, separated by ": "
-> IO ()

Print error message corresponding to the specified errno error number. This is similar to the POSIX system library function perror.

See the man page perror(3).

perror
:: StringText to precede the message, separated by ": "
-> IO ()

Print error message corresponding to the global errno error number. This is the same as the POSIX system library function perror.

See the man page perror(3).

abort
:: Typeable err
=> (err -> String)Error message generation function
-> IO aIO action to monitor
-> IO aSame action, but abort with error message in case of user exception

Execute the supplied action. In case of an error, exit with an error message.

An error is a dynamic exception, thrown using throwDyn as a type which is instance of Typeable. The type err is supposed to be a specific type used for specific errors. The program is terminated with exitFailure.

_exit
:: IntExit code
-> IO aNever returns

Forcibly terminate the program, circumventing normal program shutdown.

This is the _exit(2) system call. No cleanup actions installed with bracket are performed, no data buffered by file handles is written out, etc.

failIO :: String -> IO a

Print a message to stderr and exit with an exit code indicating an error.

failIO msg = hPutStrLn stderr msg >> exitFailure
exitcode
:: IO ()Action to modify
-> IO ExitCodeModified action

Return the exit code, instead of throwing it as a dynamic exception.

This is used to modify the error reporting behaviour of an IO action. It is used in conjunction with run or call. Normally, they throw a process status, which indicates any error as a dynamic exception. After exitcode has been applied, only termination by a signal causes an exception to be thrown. Any exit code is returned instead.

Example: ec <- exitcode $ run "foo" ["bar"]

See run, call.

throwErrno'
:: StringDescription of the location where the error occurs in the program
-> Maybe HandleOptional handle
-> Maybe FilePathOptional file name (for failing operations on files)
-> IO a

Create and throw an IOError from the current errno value, an optional handle and an optional file name.

This is an extended version of the Foreign.C.Error.throwErrno function from the GHC libraries, which additionally allows to specify a handle and a file name to include in the IOError thrown.

See Foreign.C.Error.throwErrno, Foreign.C.Error.errnoToIOError.

show_ioerror :: IOError -> String

Convert an IOError to a string.

There is an instance declaration of IOError in Show in the GHC.IOBase library, but show_ioerror produces a more readable, and more complete, message.

fill_in_filename
:: StringFile name to fill in
-> IO aIO action to modify
-> IO aModified IO action

In case the specified action throws an IOError, fill in its filename field. This way, more useful error messages can be produced.

Example:

-- Oh, the GHC libraries neglect to fill in the file name
executeFile' prog a b c =
   fill_in_filename prog $ executeFile prog a b c

See fill_in_location, add_location.

fill_in_location
:: StringLocation name to fill in
-> IO aIO action to modify
-> IO aModified IO action

In case the specified action throws an IOError, fill in its location field. This way, more useful error messages can be produced.

Example:

my_fun a b c = do
   -- ...
   fill_in_location "my_fun" $  -- Give the caller a more useful location information in case of failure
      rename "foo" "bar"
   -- ...

See fill_in_filename.

add_location
:: StringLocation name to add
-> IO aIO action to modify
-> IO aModified IO action

In case the specified action throws an IOError, add a line to its location field. This way, more useful error messages can be produced. The specified string is prepended to the old location, separating it with a newline from the previous location, if any. When using this thoroughly, you get a reverse call stack in IOErrors.

Example:

my_fun =
   add_location "my_fun" $ do
      -- ...

See fill_in_filename, fill_in_location.

Reexported Standard Library Stuff for Exception Handling
Exception
IOException
ArithException
ArrayException
AsyncException
throw
ioError
throwTo
catchJust
handle
handleJust
tryJust
evaluate
ioErrors
arithExceptions
errorCalls
dynExceptions
assertions
asyncExceptions
userErrors
throwDyn
throwDynTo
catchDyn
block
unblock
assert
finally
toDyn
fromDyn
fromDynamic
dynApply
dynApp
Typeable
TypeRep
TyCon
mkTyCon
mkAppTy
mkFunTy
applyTy
Permissions
createDirectory
removeDirectory
renameDirectory
getDirectoryContents
getCurrentDirectory
setCurrentDirectory
removeFile
renameFile
doesFileExist
doesDirectoryExist
getPermissions
setPermissions
getModificationTime
fileAccess
getProcessStatus
Produced by Haddock version 0.6