ContentsIndex
HsShellScript
Contents
Command Line Arguments
Specific External Commands
Miscellaneous
Creating temporary files and directories
Parsing and Composing Paths
Processes, Pipes
Ways of Calling External Programs
Redirecting Input and Output
Building Pipes
Error Handling
Calling a Shell, and Quoting for Shells
Reexported Standard Library Stuff for Exception Handling
Synopsis
module HsShellScript.Args
realpath :: String -> IO String
realpath_s :: String -> IO String
symlink :: String -> String -> IO ()
is_symlink :: String -> IO Bool
readlink :: String -> IO String
du :: (Integral int, Read int) => int -> String -> IO int
mkdir :: String -> IO ()
rmdir :: String -> IO ()
rm :: String -> IO ()
cd :: String -> IO ()
chmod :: [String] -> IO ()
chown :: [String] -> IO ()
cp :: String -> String -> IO ()
mv :: String -> String -> IO ()
pwd :: IO String
mt_status :: IO (Int, Int)
rename :: String -> String -> IO ()
force_rename :: String -> String -> IO ()
force_mv :: String -> String -> IO ()
force_cmd :: (String -> String -> IO ()) -> String -> String -> IO ()
readlink' :: String -> IO String
fdupes :: [String] -> [String] -> IO [[[String]]]
zeros :: Int -> Int -> String
chomp :: String -> String
lazy_contents :: String -> IO (String, Handle)
contents :: String -> IO String
path_exists :: String -> IO Bool
path_exists' :: String -> IO Bool
is_dir :: String -> IO Bool
is_file :: String -> IO Bool
getFileStatus' :: FilePath -> IO FileStatus
fileAccess' :: FilePath -> Bool -> Bool -> Bool -> IO Bool
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
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
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
call :: IO () -> IO ()
spawn :: IO () -> IO ProcessID
run :: FilePath -> [String] -> IO ()
exec :: String -> [String] -> IO ()
execp :: String -> [String] -> IO ()
exece :: String -> [String] -> [(String, String)] -> IO ()
execpe :: String -> [String] -> [(String, String)] -> IO ()
echo :: (FilePath -> [String] -> IO ()) -> FilePath -> [String] -> IO ()
system_throw :: String -> IO ()
(->-) :: 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 ()
out_to_err :: IO ()
(-|-) :: 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
pipe_from :: IO a -> IO String
lazy_pipe_from :: IO a -> IO (String, IO ProcessStatus)
h_pipe_from :: IO a -> IO Handle
pipe_from2 :: IO a -> IO String
lazy_pipe_from2 :: IO a -> IO (String, IO ProcessStatus)
h_pipe_from2 :: IO a -> IO Handle
pipes :: IO a -> Bool -> Bool -> Bool -> IO (Maybe Handle, Maybe Handle, Maybe Handle, ProcessID)
errno :: IO Errno
strerror :: Errno -> IO String
perror' :: Errno -> String -> IO ()
perror :: String -> IO ()
abort :: (Typeable err) => (err -> String) -> IO a -> IO a
failIO :: String -> IO a
exitcode :: IO () -> IO ExitCode
throwErrno' :: String -> Maybe Handle -> Maybe FilePath -> IO a
show_ioerror :: IOError -> String
mainwrapper :: IO a -> IO a
shell_command :: String -> [String] -> String
shell_quote :: String -> String
quote0 :: String -> String
quote :: String -> String
Command Line Arguments
module HsShellScript.Args
Specific External Commands
realpath
:: String path
-> IO String noramlized, 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 (such as path not found), an IOError is thrown. The path is included in the IOError and can be accessed with ioeGetFileName from the Haskell standard library IO.
realpath_s
:: String path
-> IO String noramlized, absolute path, with symbolic links not expanded
Call the realpath program, with the -s option. Return its output. See realpath(1).
symlink
:: String contents of the symlink (from)
-> String path 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.
is_symlink
:: String path
-> IO Bool Whether the path is a symbolic link.
Determine whether a path is a symbolic link. The path must exist in the file system. Otherwise, an IOError which makes isDoesNotExistError (from the standard library IO) true will be thrown. Its GHC IOErrorType is NoSuchThing. However, the symlink may dangle. In this case the result is True. This function calls readlink(2). Any error which may occur, results in an IOError thrown. The path is included in the IOError and can be accessed with ioeGetFileName from the Haskell standard library IO.
readlink
:: String Path of the symbolic link
-> IO String The 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".
du
:: (Integral int, Read int)
=> int block size, this is the --block-size option.
-> String path of the file or directory to determine the size of
-> IO int size in blocks
Call the du program. See du(1).
mkdir
:: String path
-> IO ()
Create directory. This is 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
:: String path
-> 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.
rm
:: String path
-> IO ()
Remove file. This is Directory.removeFile from the Haskell standard library. In case of an error, the path is included in the IOError, which GHC's implementation neglects to do.
cd
:: String path
-> 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.
chmod
:: [String] Command line arguments
-> IO ()

Execute binchmod

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

Execute binchown

chown = run "/bin/chown"
cp
:: String source
-> String destination
-> IO ()
Execute the cp program
mv
:: String source
-> String destination
-> IO ()
Execute the mv program
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.
mt_status
:: IO (Int, Int) file and block number
Run the command mt status for querying the tape drive status, and parse its output.
rename
:: String Old path
-> String New path or target directory
-> 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 a directories. Failure results in an IOError thrown. The new path is included in the IOError and can be accessed with ioeGetFileName from the Haskell standard library IO.
force_rename
:: String Old path
-> String New path or target directory
-> 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
:: String Old path
-> String New path
-> 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 = force_cmd (\src tgt -> run "/bin/mv" ["--", src, tgt]) src tgt

See force_cmd, force_mv.

force_cmd
:: (String -> String -> IO ()) Command to execute after preparing the permissions
-> String Old path
-> String New 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.

readlink'
:: String path of the symbolic link
-> IO String target; 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, it 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, an IOError is thrown. The path is included in the IOError and can be accessed with ioeGetFileName from the Haskell standard library IO.
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 paths are normalised (using normalise_path).

Miscellaneous
zeros
:: Int how many characters to fill up
-> Int value to represent as a string
-> String string representation of the value, using the specified number of characters
Format an Int with leading zeros.
chomp
:: String String to be chomped
-> String same string, except for no newline characters at the end
Remove trailing newlines. This is silimar to perl's chomp procedure.
lazy_contents
:: String either the name of a file, or "-"
-> IO (String, Handle) The contents of the file (or stdin) and the handle used to read it.

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

The returned handle must be closed eventually, or the process will run out of file handles.

contents
:: String either the name of a file, or "-" for stdin
-> IO String the 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.
path_exists
:: String Path
-> IO Bool Whether 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'
:: String Path
-> IO Bool Whether 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_dir
:: String Path
-> IO Bool Whether the path exists and points to a directory.
Test if path points to a directory. This is a shortcut for Directory.doesDirectoryExist.
is_file
:: String Path
-> IO Bool Whether the path exists and points to a file.
Test if path points to a file. This is a shortcut for Directory.doesFileExist.
getFileStatus'
:: FilePath Path of the file, whose status is to be queried
-> IO FileStatus Status 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.

Creating temporary files and directories
tmp_file
:: String Prefix for the path to generate.
-> IO FilePath Path 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
:: String Prefix for the path to generate.
-> IO FilePath Path 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
:: Int Number of random characters to intersperse. Must be large enough, such that most combinations can't already exist.
-> String Prefix for the path to generate.
-> String Suffix for the path to generate.
-> IO FilePath Path 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
:: Int Number of random characters to intersperse. Must be large enough, such that most combinations can't already exist.
-> String Prefix for the path to generate.
-> String Suffix for the path to generate.
-> IO FilePath Generated 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
:: Int Number of random characters to intersperse. Must be large enough, such that most combinations can't already exist.
-> String Prefix for the path to generate.
-> String Suffix for the path to generate.
-> IO FilePath Generated 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
:: String Prefix for the path to generate.
-> (Handle -> IO a) Action to perform.
-> IO a Returns 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
:: String Prefix for the path to generate.
-> (FilePath -> IO a) Action to perform.
-> IO a Returns 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
:: Int Number of random characters to intersperse. Must be large enough, such that most combinations can't already exist.
-> String Prefix for the path to generate.
-> String Suffix for the path to generate.
-> (Handle -> IO a) Action to perform.
-> IO a Returns 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
:: Int Number of random characters to intersperse. Must be large enough, such that most combinations can't already exist.
-> String Prefix for the path to generate.
-> String Suffix for the path to generate.
-> (FilePath -> IO a) Action to perform.
-> IO a Returns 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.

Parsing and Composing Paths
slice_path
:: String The 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 probably an error. However, it is treated like ".", yielding an empty path components list.

Examples:

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

See unslice_path, realpath, realpath_s.

unslice_path
:: [String] List of path components
-> String The 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.

See slice_path.

normalise_path
:: String Path to be normalised
-> String Path 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.

normalise_path = unslice_path . slice_path

slice_filename
:: String Path
-> [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'
:: String File 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
-> String Name 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
:: String Path 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
-> String Path formed from the directory and file name parts

Inverse of split_path, except for normalisation.

This concatenates two paths, and takes care of "." and empty paths. When the two components are the result of split_path, then unsplit_path creates a normalised path. It is best documented by its definition:

unsplit_path (".", "") = "."
unsplit_path ("", ".") = "."
unsplit_path (".", q)  = q
unsplit_path ("", q)   = q
unsplit_path (p, "")   = p
unsplit_path (p, ".")  = p
unsplit_path (p, q)    = p ++ "/" ++ q

Examples:

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

See split_path.

split_filename
:: String Path 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'
:: String Filename 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
-> String Path

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
:: String Path 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
-> String Path consisting of dir, base and suffix
Form path from directory, base file name and suffix parts.
test_suffix
:: String Suffix to split off
-> String Path to test
-> Maybe String Prefix 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
:: String The path to be made absolute
-> IO String Absulte 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
:: String The directory relative to which the path is made absolute
-> String The path to be made absolute
-> String Absolute 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'
:: String The path to be made absolute
-> String The directory relative to which the path is made absolute
-> String Absolute 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
:: String Path to be normalised
-> Maybe String In 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
Processes, Pipes
Ways of Calling External Programs
call
:: IO () action 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. It can then be catched with the catchDyn function from the GHC Exception library.

The waitpid call is done blockingly. It waits if the program has been stopped.

spawn
:: IO () Action to execute as a child process.
-> IO ProcessID Process 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 and performs the specified action. The process ID is returned. It can be used in conjunction with getProcessStatus.

run :: FilePath -> [String] -> 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.

If the program exits in a way which indicates an error, the ProcessStatus is thrown as a dynamic exception, which can be catched with the catchDyn function from the GHC Exception library.

This is a frontend to call.

See call.

exec
:: String Full path to the executable
-> [String] Command line arguments
-> IO ()

Execute an external program. This replaces the running process. The path is not searched, and the environment is not changed.

This is a shorthand for executeFile' ... False ... Nothing.

See man page exec(3).

execp
:: String Name or path of the executable
-> [String] Command line arguments
-> IO ()

Execute an external program. This replaces the running process. The PATH is searched, the environment is not changed.

This is a shorthand for executeFile' ... True ... Nothing.

See man page exec(3).

exece
:: String Full path to the executable
-> [String] Command line arguments
-> [(String, String)] Environment
-> IO ()

Execute an external program. This replaces the running process. The path is not searched. The environment is set to the specified value.

This is a shorthand for executeFile' ... False ... (Just ...).

See man page exec(3).

execpe
:: String Name or path of the executable
-> [String] Command line arguments
-> [(String, String)] Environment
-> IO ()

Execute an external program. This replaces the running process. The PATH is searched. The environment is set to the specified value.

This is a shorthand for executeFile' ... True ... (Just ...).

See man page exec(3).

echo
:: (FilePath -> [String] -> IO ()) Action to perform
-> FilePath Name 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 run, before running it.

See run, call, exec.

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]
Redirecting Input and Output
(->-)
:: IO a Action, whose output will be redirected
-> FilePath File to redirect the output to
-> IO a Result action

Redirect stdout to a file. This modifies the specified action, such that the standard output is redirected to a file. This will also affect all subsequently executed actions, if the action isn't called in a separate process. The file will be overwritten, if it already exists.

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

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

(->>-)
:: IO a Action, whose output will be redirected
-> FilePath File to redirect the output to
-> IO a Result action

Redirect stdout to a file. This modifies the specified action, such that the standard output is redirected to a file. This will also affect all subsequently executed actions, if the action isn't called in a separate process. If the file already exists, the output will be appended.

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

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

(=>-)
:: IO a Action, whose error output will be redirected
-> FilePath File to redirect the error output to
-> IO a Result action

Redirect stderr to a file. This modifies the specified action, such that the standard error output is redirected to a file. This will also affect all subsequently executed actions, if the action isn't called in a separate process. The file will be overwritten, if it already exists.

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

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

(=>>-)
:: IO a Action, whose error output will be redirected
-> FilePath File to redirect the error output to
-> IO a Result action

Redirect stderr to a file. This modifies the specified action, such that the standard error output is redirected to a file. This will also affect all subsequently executed actions, if the action isn't called in a separate process. If the file already exists, the output will be appended.

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. This will also affect all subsequently executed actions, if the action isn't called in a separate process.

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

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

(-&>-)
:: IO a Action, whose output and error output will be redirected
-> FilePath File to redirect to
-> IO a Result 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. This will also affect all subsequently executed actions, if the action isn't called in a separate process.

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

(-&>-) io path = (err_to_out >> io) (->-) path 
(-&>>-)
:: IO a Action, whose output and error output will be redirected
-> FilePath File to redirect to
-> IO a Result action

Redirect both stdout and stderr to a file. If the file already exists, the output will be appended. This will also affect all subsequently executed actions, if the action isn't called in a separate process.

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

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

Send the error output of the current process to its standard output. This will affect all subsequent IO actions.

err_to_out = dupTo (intToFd 1) (intToFd 2) >> return ()
out_to_err :: IO ()

Send the standard output of the current process to its error output. This will affect all subsequent IO actions.

out_to_err = dupTo (intToFd 2) (intToFd 1) >> return ()
Building Pipes
(-|-)
:: IO a Action which won't be forked
-> IO b Action which will be forked and connected with a pipe
-> IO a Result 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.

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

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

(=|-)
:: IO a Action which won't be forked
-> IO b Action which will be forked and connected with a pipe
-> IO a Result 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.

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

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

(-|=)
:: IO a Action which will be forked and connected with a pipe
-> IO b Action which won't be forked
-> IO b Result 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.

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

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

(=|=)
:: IO a Action which will be forked and connected with a pipe
-> IO b Action which won't be forked
-> IO b Result 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.

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

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

pipe_to
:: String Text to pipe
-> IO a Action 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 standard input. The pipe is closed afterwards.

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

See call, run, -<-, h_pipe_to.

h_pipe_to
:: IO a Action to run as a separate process, and to pipe to
-> IO Handle

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

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

See call, run, -<-, pipe_to, pipe_from, pipe_from2.

pipe_from
:: IO a Action to run as a separate process
-> IO String The called program's standard output.

Run an IO action as a separate process, and read its standard output immediately. This is like the backquote feature of shells.

If the process exits in any way which indicates an error, the ProcessStatus is thrown as a dynamic exception.

Example: txt <- pipe_from $ exec "/usr/bin/foo" ["bar"]

See call, run, -<-, pipe_to, pipe_from2

lazy_pipe_from
:: IO a Action to run as a separate process
-> IO (String, IO ProcessStatus) The action's lazy output and the close action

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

The result is the process' output and a close action, which must be called after the caller has finished reading. It closes the pipe and waits for the child process to finish. If not all of the program's output has been read yet, the operating system will send it a SIGPIPE signal, which causes the child to terminate, unless it catches this signal. The close action returns the child process' process status, which is either Exited exitCode or Terminated signal.

Example:

do (txt,close) <- lazy_pipe_from $ exec "/usr/bin/foo" ["bar"]
   ...
   close

See call, run, -<-, pipe_to, pipe_from2

h_pipe_from
:: IO a Action to run as a separate process
-> IO Handle

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

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

See call, run, -<-, pipe_to, pipe_from2

pipe_from2
:: IO a Action to run as a separate process
-> IO String The called program's standard error output.

Run an IO action as a separate process, and read its standard error output immediately.

If the process exits in any way which indicates an error, the ProcessStatus is thrown as a dynamic exception.

Example: err <- pipe_from $ exec "/usr/bin/foo" ["bar"]

See call, run, -<-, pipe_to, pipe_from

lazy_pipe_from2
:: IO a Action to run as a separate process
-> IO (String, IO ProcessStatus) The action's lazy error output and the close action

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

The result is the process' error output and a close action, which must be called after the caller has finished reading. It closes the pipe and waits for the child process to finish. If not all of the program's error output has been read yet, the operating system will send it a SIGPIPE signal, which causes the child to terminate, unless it catches this signal. The close action returns the child process' process status, which is either Exited exitCode or Terminated signal.

Example:

do (err,close) \<- lazy_pipe_from2 $ exec "/usr/bin/foo" ["bar"]
   ...
   close

See call, run, -<-, pipe_to, pipe_from

h_pipe_from2
:: IO a Action to run as a separate process, and to connect with a pipe
-> IO Handle

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

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

See call, run, -<-, pipe_to, pipe_from.

pipes
:: IO a Action to run in a new process
-> Bool Whether to make stdin pipe
-> Bool Whether to make stdout pipe
-> Bool Whether 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 standard input, its standard output and its standard error output with pipes.

See pipe_from, pipe_from2, pipe_to.

Error Handling
errno
:: IO Errno errno value
Read the global system error number. This is the POSIX errno value. This function is redundant. Use Foreign.C.Error.getErrno instead.
strerror
:: Errno errno value
-> IO String Corresponding 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'
:: Errno errno error number
-> String Text to precede the message, separated by ": "
-> IO ()

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

See the man page perror(3).

perror
:: String Text 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 a IO action to monitor
-> IO a Same action, but abort with error message in case of user exception

Execute the supplied action. In case of an error, print a message and exit.

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.

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 ExitCode Modified 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. An exit code which indicates failure, instead is returned.

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

See run, call.

throwErrno'
:: String Description of the location where the error occurs in the program
-> Maybe Handle Optional handle
-> Maybe FilePath Optional 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.

mainwrapper
:: IO a Should be main
-> IO a Wrapped main

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

main = mainwrapper main'

The exceptions caught are ArgError, ProcessStatus, IOError.

Calling a Shell, and Quoting for Shells
shell_command
:: String name or path of the executable
-> [String] command line arguments
-> String shell 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.
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."

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.

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.

See quote0, shell_quote.

Reexported Standard Library Stuff for Exception Handling
Produced by Haddock version 0.4