Safe Haskell | Safe-Inferred |
---|---|
Language | Haskell2010 |
- Command Line Arguments
- Paths and Directories
- Symbolic Links
- Manipulating Files
- Interfaces to Some Specific External Commands
- Calling External Programs
- Redirecting Input and Output
- Pipes
- Shell-like Quoting
- Creating temporary files and directories
- Reading mount information
- Output to the standard stream, colorful logging and error reporting
- Miscellaneous
- Error 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
- module HsShellScript.Paths
- 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, Show int) => int -> String -> IO int
- subproc :: IO a -> IO ()
- spawn :: IO a -> IO ProcessID
- runprog :: FilePath -> [String] -> IO ()
- data RunError = RunError {}
- show_runerror :: RunError -> String
- to_ioe :: RunError -> IOError
- as_ioe :: IO a -> IO a
- 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_runprog :: String -> IO ()
- system_throw :: String -> IO ()
- execute_file :: FilePath -> Bool -> [String] -> Maybe [(String, String)] -> IO a
- child :: IO a -> IO b
- explain_processstatus :: ProcessStatus -> String
- call :: IO a -> IO ()
- run :: FilePath -> [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 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
- redirect :: Handle -> Handle -> IO a -> IO a
- 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
- lazy_pipe_from2 :: IO a -> IO (String, ProcessID)
- h_pipe_from2 :: IO a -> IO (Handle, ProcessID)
- pipe_from_full :: IO a -> IO (String, ProcessStatus)
- pipe_from_full2 :: IO a -> IO (String, ProcessStatus)
- pipes :: IO a -> Bool -> Bool -> Bool -> IO (Maybe Handle, Maybe Handle, Maybe Handle, ProcessID)
- module HsShellScript.Shell
- 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 {}
- read_mounts :: String -> IO [Mntent]
- read_mtab :: IO [Mntent]
- read_fstab :: IO [Mntent]
- outm :: String -> IO ()
- outm_ :: String -> IO ()
- logm :: String -> IO ()
- logm_ :: String -> IO ()
- errm :: String -> IO ()
- errm_ :: String -> IO ()
- isatty :: Handle -> IO Bool
- terminal_width :: Handle -> IO (Maybe Int)
- terminal_width_ioe :: Handle -> IO Int
- zeros :: Int -> Int -> String
- chomp :: String -> String
- lazy_contents :: String -> IO String
- contents :: String -> IO String
- glob :: String -> IO [String]
- glob_quote :: String -> String
- mainwrapper :: IO a -> IO a
- errno :: IO Errno
- strerror :: Errno -> IO String
- perror' :: Errno -> String -> IO ()
- perror :: String -> IO ()
- _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
Command Line Arguments
module HsShellScript.Args
Paths and Directories
Create directory. This is a shorthand to System.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.
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.
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.
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.)
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.
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
.
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
.
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
.
Test if path points to a file. This is a shortcut for
Directory.doesFileExist
.
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
.
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
module HsShellScript.Paths
Symbolic Links
Determine whether a path is a symbolic link. The result for a dangling symlink is True
. The path must exist
in the file system. In case of an error, a proper IOError
is thrown.
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
.
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".
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
Remove file. This is Directory.removeFile
from the Haskell standard library, which is a direct frontend to
the unlink(2)
system call in GHC.
Execute the mv program.
This calls the /bin/mv
to rename a file, or move it to another directory. You can move a file to another
file system with this. This starts a new process, which is rather slow. Consider using rename
instead, when
possible.
See rename
.
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 a file or directory, and manage 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
Move a file or directory, and manage 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
Rename a file with rename
, or when necessary with mv
, and manage 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
:: (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 manage 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 concerns itself 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
.
:: String | File or directory to make writeable |
-> IO a | Action to perform |
-> IO a | Returns 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
.
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
.
:: 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 Source #
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 () Source #
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
Run the command mt status
for querying the tape drive status, and
parse its output.
:: [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
).
:: (Integral int, Read int, Show int) | |
=> int | block size, this is the |
-> String | path of the file or directory to determine the size of |
-> IO int | size 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 (subproc
, 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). 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 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 deal 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 Forking Child Processes and Calling External Programs
Execute an IO action as a separate process, and wait for it to finish. Report errors as exceptions.
This forks a child process, which performs the specified IO action. In case the child process has been stopped by a signal, the parent blocks.
If the action throws an IOError
, it is transmitted to the parent.
It is then raised there, as if it happened locally. The child then aborts
quietly with an exit code of 0.
Exceptions in the child process, other than IOError
s, result in an error
message on stderr
, and a ProcessStatus
exception in the parent, with the
value of Exited (ExitFailure 1)
. The following exceptions are understood by
subproc
, and result in corresponding messages: ArgError
, ProcessStatus
,
RunError
, IOError
and ExitCode
. Other exceptions result in the generic
message, as produced by show
.
If the child process exits with an exit code other than zero, or it is
terminated by a signal, the corresponding ProcessStatus
is raised as an
exception in the parent program. Only IOError
s are transmitted to the parent.
When used in conjunction with an exec
variant, this means that the parent
process can tell the difference between failure of the exec
call itself,
and failure of the child program being executed after a successful call of
the exec
variant. In case of failure of the exec
call, You get the IOError
, which
happened in the child when calling executeFile
(from the GHC hierarchical
libraries). In case of the called program failing, you get the ProcessStatus
.
Unless you replace the child process, calling an exec
variant, the child
should let the control flow leave the action normally (unless it throws an
IOError
). The child process is then properly terminated by subproc
, such
that no resources, which have been duplicated by the fork, cause problems.
See HsShellScript for details.
If you want to run an external program, by calling one of the exec
variants in the child action, you might want to call runprog
instead of subproc
.
Examples:
Run a program with the environment replaced:
subproc (execpe "foobar" ["1","2","3"] new_env)
This results in a ProcessStatus
exception:
subproc (exec "/bin/false" [])
This results in an IOError
(unless you actually have /frooble
):
subproc (exec "/frooble" [])
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 for further details.
See subproc
.
Run an external program, and report errors as exceptions. The executable is
searched via the PATH
. In case the child process has been stopped by a
signal, the parent blocks.
In case the program exits in an way which indicates an error, or is
terminated by a signal, a RunError
is thrown. It
contains the details of the call. The runprog
action can also be converted
to throw IOError
s instaed, by applying as_ioe
to it. Either can be used
to generate an informative error message.
In case of starting the program itself failed, an IOError
is thrown.
runprog prog par
is a simple front end to subproc
. It is essentially
subproc (execp prog par)
, apart from building a RunError
from a
ProcessStatus
.
Example 1:
do runprog "foo" ["some", "args"] ... `catch` (\re -> do errm (show_runerror re) ... )
Example 2:
do as_ioe $ runprog "foo" ["some", "args"] ... `catch` (\ioe -> do errm (show_ioerror ioe) ... )
See subproc
, spawn
, RunError
, show_runerror
, to_ioe
, as_ioe
.
An error which occured when calling an external program. The fields specifiy the details of the call.
See show_runerror
, to_ioe
, as_ioe
, System.Posix.ProcessStatus
.
Instances
Exception RunError Source # | |
Defined in HsShellScript.ProcErr toException :: RunError -> SomeException # fromException :: SomeException -> Maybe RunError # displayException :: RunError -> String # | |
Show RunError Source # | |
Eq RunError Source # | |
show_runerror :: RunError -> String Source #
Make a readable error message. This includes all the
fields of RunError
except for the environment.
See RunError
.
to_ioe :: RunError -> IOError Source #
Convert a RunError
to an IOError
.
The IOError
type isn't capable of holding all the information which is
contained in a RunError
. The environment is left out, and most of the other
fields are included only informally, in the description.
The fields of the generated IOError
are:
- The handle (
ioeGetHandle
):Nothing
- The error type (
ioeGetErrorType
):GHC.IO.Exception.SystemError
ioe_location
:"runprog"
ioe_description
: The error message, as procuded byshow_runerror
.ioe_filename
: This isJust (shell_command prog pars)
, with prog and pars being the program and its arguments.
See as_ioe
, runprog
, show_runerror
.
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.
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.
:: String | Full path to the executable |
-> [String] | Command line arguments |
-> [(String, String)] | New environment |
-> IO a | Never 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.
:: String | Name or path of the executable |
-> [String] | Command line arguments |
-> [(String, String)] | New environment |
-> IO a | Never 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.
:: IORef String | A 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 runprog
does. If the subroutine
throws an (Exited ec)
exception (of type ProcessStatus
), such as thrown by
runprog
, 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
.
When it throws an exception, an error message is printed and it is terminated
with _exit
0
. See HsShellScript for details._exit
1
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"] `catch` (handler msgref)
See lazy_pipe_from
, subproc
, runprog
, Data.IORef.
system_runprog :: String -> IO () Source #
Call the shell to execute a command. In case of an error, a RunError
ist thrown. This is like the Haskell
standard library function system
, except that error handling is brought in accordance with HsShellScript's
scheme. (It is not a front end to system
.)
system_runprog cmd = runprog "/bin/sh" ["-c", "--", cmd]
Example: Call "foo" and report Errors as IOError
s, rather than RunError
s.
as_ioe $ system_runprog "foo" ["bar", "baz"]
system_throw :: String -> IO () Source #
Call the shell to execute a command. In case of an error, throw the ProcessStatus
(such as (Exited
(ExitFailure ec))
) as an 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 lead to the ProcessStatus
being thrown. 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]
This function is deprecated. You should rather use system_runprog
, which provides for much better error
reporting.
:: FilePath | Program to call |
-> Bool | Search |
-> [String] | Arguments |
-> Maybe [(String, String)] | Optionally new environment |
-> IO a | Never 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
:
stdout
andstderr
are flushed.- 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.
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.
- 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. - 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.
- 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.
- In any IOError, the program is filled in as the file name (
executeFile
neglects this). - The return type is a generic
a
, rather than()
.
Also see HsShellScript.
child :: IO a -> IO b Source #
Modify a subroutine action in order to make it suitable to run as a child process.
This is used by functions like call
, silently
, pipe_to
etc. The action
is executed. When it returns, the (child) process is terminated with
(after flushing _exit
0stdout
), circumventing normal program shutdown. When it
throws an exception, an error message is printed and the (child) process is
terminated with
._exit
1
Execute an IO action as a separate process, and wait for it to finish. Report errors as exceptions.
This function is included only for backwards compatibility. New code should
use subproc
instead, which has better error handling.
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.
The parent process waits for the child processes, which have been stopped by a signal.
See HsShellScript for further details.
Run an external program. This starts a program as a child
process, and waits for it to finish. The executable is searched via the
PATH
.
This function is included for backwards compatibility only. New code should
use runprog
, which has much better error handling.
When the specified program can't be executed, an error message is printed, and the main process
gets a ProcessStatus
thrown, 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. However, an error message "Error calling ...", including the description in the IOError produced
by the failed execp
call, is printed on stderr
.
run prog par
is essentially call (execp prog par)
.
Example:
run "/usr/bin/foobar" ["some", "args"] `catch` (\ps -> do -- oops... )
Redirecting Input and Output
:: IO a | Action, whose output will be redirected |
-> FilePath | File to redirect the output to |
-> IO a | Result 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 and
HsShellScript for details.
The file is written in text mode. This means that the
output is converted from Unicode to the system character set, which
is determined by the environment variable LANG
.
Example:
runprog "/some/program" [] ->- "/tmp/output"
Note: You can't redirect to "/dev/null"
this way, because GHC 6.4's openFile
throws an "invalid argument"
IOError. (This may be a bug in the GHC 6.4 libraries). Use ->>-
instead.
:: IO a | Action, whose output will be redirected |
-> FilePath | File to redirect the output to |
-> IO a | Result 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 and
HsShellScript for details.
The file is written in text mode. This means that the
output is converted from Unicode to the system character set, which
is determined by the environment variable LANG
.
Example:
run "/some/noisy/program" [] ->>- "/dev/null"
:: IO a | Action, whose error output will be redirected |
-> FilePath | File to redirect the error output to |
-> IO a | Result 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 and
HsShellScript for details.
Note: You can't redirect to "/dev/null"
this way, because GHC 6.4's openFile
throws an "invalid argument"
IOError. (This may be a bug in the GHC 6.4 libraries). Use =>>-
instead.
The file is written in text mode. This means that the
output is converted from Unicode to the system character set, which
is determined by the environment variable LANG
.
Example:
run "/path/to/foo" [] =>- "/tmp/errlog"
:: IO a | Action, whose error output will be redirected |
-> FilePath | File to redirect the error output to |
-> IO a | Result 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 and
HsShellScript for details.
The file is written in text mode. This means that the
output is converted from Unicode to the system character set, which
is determined by the environment variable LANG
.
Example:
run "/some/program" [] =>>- "/dev/null"
(-<-) :: IO a -> FilePath -> IO a infixl 3 Source #
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 and
HsShellScript for details.
The file is read in text mode. This means that the input is converted from the
system character set to Unicode. The system's character set is determined by the
environment variable LANG
.
Example:
subproc (exec "/path/to/foo" [] -<- "bar")
:: 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.
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 and
HsShellScript for details.
Note: You can't redirect to "/dev/null"
this way, because GHC 6.4's openFile
throws an "invalid argument"
IOError. (This may be a bug in the GHC 6.4 libraries). Use -&>>-
instead.
The file is written in text mode. This means that the
output is converted from Unicode to the system character set, which
is determined by the environment variable LANG
.
(-&>-) io path = err_to_out io ->- path
Example:
subproc (exec "/path/to/foo" [] -&>- "log")
See (-&>>-)
, err_to_out
.
:: 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.
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 and
HsShellScript for details.
The file is written in text mode. This means that the
output is converted from Unicode to the system character set, which
is determined by the environment variable LANG
.
(-&>>-) io path = (err_to_out >> io) ->>- path
Example:
run "/some/noisy/program" [] -&>>- "/dev/null"
See (-&>-)
, out_to_err
.
err_to_out :: IO a -> IO a Source #
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 and
HsShellScript for details.
err_to_out = redirect stderr stdout
See redirect
.
out_to_err :: IO a -> IO a Source #
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 and
HsShellScript for details.
redirect stdout stderr
See redirect
.
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.
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 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 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
. The
pipe is called "left handed", because p
remains unforked, 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.
The pipe, which connects p
and q
, is in text mode. This means that the
output of p
is converted from Unicode to the system character set, which
is determined by the environment variable LANG
.
See HsShellScript and HsShellScript for further details.
Examples:
subproc (exec "/usr/bin/foo" [] -|- exec "/usr/bin/bar" [])
sunproc ( execp "foo" ["..."] -|= ( -- Do something with foo's output do cnt <- lazy_contents "-" ... ) )
sunproc ( err_to_out foo -|- exec "/usr/bin/tee" ["-a", "/tmp/foo.log"] )
:: 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
. 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.
The pipe, which connects p
and q
, is in text mode. This means that the
output of p
is converted from Unicode to the system character set, which
is determined by the environment variable LANG
.
See HsShellScript and HsShellScript for further details.
Example:
subproc (exec "/usr/bin/foo" [] =|- exec "/usr/bin/bar" [])
:: 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
.
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.
The pipe, which connects p
and q
, is in text mode. This means that the
output of p
is converted from Unicode to the system character set, which
is determined by the environment variable LANG
.
See HsShellScript and HsShellScript for further details.
Example:
subproc (exec \"\/usr\/bin\/foo\" [] -|= exec \"\/usr\/bin\/bar\" [])
:: 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
.
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.
The pipe, which connects p
and q
, is in text mode. This means that the
output of p
is converted from Unicode to the system character set, which
is determined by the environment variable LANG
.
See HsShellScript and HsShellScript for further details.
Example:
subproc (exec "/usr/bin/foo" [] =|= exec "/usr/bin/bar" [])
Temporarily replace a handle. This makes a backup copy of the original handle (typically a standard handle), overwrites it with the specified one, runs the specified action, and restores the handle from the backup.
Example:
h <- openFile "/tmp/log" WriteMode redirect stdout h io hClose h
This is the same as
io ->- "/tmp/log"
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.
This forks a child process, which executes the specified action. The specified
text is sent to the action's stdin
through a pipe. Then the pipe is closed.
In case the action replaces the process by calling an exec
variant, it is
made sure that the process gets the text on it's file descriptor 0.
In case the action fails (exits with an exit status other than 0, or is
terminated by a signal), the ProcessStatus
is thrown, such as reported by
getProcessStatus
. No attempt is made to create more meaningful
exceptions, like it is done by runprog
/subproc
.
Exceptions in the action result in an error message on stderr
, and the
termination of the child. The parent gets a ProcessStatus
exception, with
the value of Exited (ExitFailure 1)
. The following exceptions are
understood, and result in corresponding messages: ArgError
,
ProcessStatus
, RunError
, IOError
and ExitCode
. Other exceptions
result in the generic message, as produced by show
.
Unless you replace the child process, calling an exec
variant, the child
should let the control flow leave the action normally.
The child process is then properly terminated, such
that no resources, which have been duplicated by the fork, cause problems.
See HsShellScript for details.
The pipe is set to text mode. This means that the Unicode characters in
the text are converted to the system character set. If you need to pipe binary
data, you should use h_pipe_to
, and set the returned handle to binary
mode. This is accomplished by
. The system
character set is determined by the environment variable hSetBinaryMode
h TrueLANG
.
Example:
pipe_to "blah" (exec "/usr/bin/foo" ["bar"])
Example: Access both stdin
and stdout
of an external program.
import HsShellScript main = mainwrapper $ do res <- pipe_from $ pipe_to "2\n3\n1" $ exec "/usr/bin/sort" [] putStrLn res
:: IO a | Action 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 get a connection (a pipe) to
its stdin
as a file handle.
This forks a subprocess, which executes the specified action. A file handle,
which is connected to its stdin
, is returned. The child's ProcessID
is returned as well. If the action replaces the child process, by calling an
exec
variant, it is made sure that its file descriptor 0 is connected to
the returned file handle.
This gives you full control of the pipe, and of the forked process. But you need to deal with the child process by yourself.
Unless you replace the child process, calling an exec
variant, the child
should let the control flow leave the action normally.
The child process is then properly terminated, such
that no resources, which have been duplicated by the fork, cause problems.
See HsShellScript for details.
Errors can only be detected by examining the child's process status (using
getProcessStatus
). If the child action throws an
exception, an error message is printed on stderr
, and the child process
exits with a ProcessStatus
of Exited
(ExitFailure 1)
. The following exceptions are understood, and
result in corresponding messages: ArgError
, ProcessStatus
, RunError
,
IOError
and ExitCode
. Other exceptions result in the generic message, as
produced by show
.
If the child process exits in a way which signals an error, the
corresponding ProcessStatus
is returned by getProcessStatus
. See
getProcessStatus
for details.
The pipe is set to text mode. This means that the Unicode characters in the
text are converted to the system character set. You can set the returned
handle to binary mode, by calling
. The system
character set is determined by the environment variable hSetBinaryMode
handle TrueLANG
.
Example:
(handle, pid) <- h_pipe_to $ exec "/usr/bin/foo" ["bar"] hPutStrLn handle "Some text to go through the pipe" (Just ps) <- getProcessStatus True False pid when (ps /= Exited ExitSuccess) $ throw ps
See -<-
, pipe_to
, pipe_from
, pipe_from2
. See HsShellScript for more details.
:: IO a | Action to run as a separate process. Its return value is ignored. |
-> IO String | The action'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.
This forks a child process, which executes the specified action. The output
of the child is read from its standard output. In case it replaces the
process by calling an exec
variant, it is make sure that the output is
read from the new process' file descriptor 1.
The end of the child's output is reached when either the standard output is closed, or the child process exits. The program blocks until the action exits, even if the child closes its standard output earlier. So the parent process always notices a failure of the action (when it exits in a way which indicates an error).
When the child action exits in a way which indicates an error, the
corresponding ProcessStatus
is thrown. See
getProcessStatus
. No attempt is made to create more
meaningful exceptions, like it is done by runprog
/subproc
.
Exceptions in the action result in an error message on stderr
, and the
proper termination of the child. The parent gets a ProcessStatus
exception, with
the value of Exited (ExitFailure 1)
. The following exceptions are
understood, and result in corresponding messages: ArgError
,
ProcessStatus
, RunError
, IOError
and ExitCode
. Other exceptions
result in the generic message, as produced by show
.
Unless you replace the child process, calling an exec
variant, the child
should let the control flow leave the action normally. The child process is
then properly terminated, such that no resources, which have been duplicated
by the fork, cause problems. See HsShellScript for details.
Unlike shells' backquote feature, pipe_from
does not remove any trailing
newline characters. The entire output of the action is returned. You might want
to apply chomp
to the result.
The pipe is set to text mode. This means that the Unicode characters in the
text, which is read from stdin, is converted from the system character set to
Unicode. The system character set is determined by the environment variable
LANG
. If you need to read binary data from the forked process, you should use
h_pipe_from
and set the returned handle to binary mode. This is
accomplished by
.hSetBinaryMode
h True
Example:
output <- pipe_from $ exec "/bin/mount" []
Example: Access both stdin
and stdout
of an external program.
import HsShellScript main = mainwrapper $ do res <- pipe_from $ pipe_to "2\n3\n1" $ exec "/usr/bin/sort" [] putStrLn res
See exec
, pipe_to
, pipe_from2
, h_pipe_from
, lazy_pipe_from
, chomp
, silently
.
:: IO a | Action 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 in a separate process, and read its standard output, The output is read lazily, as the returned string is evaluated. The child's output along with its process ID are returned.
This forks a child process, which executes the specified action. The output
of the child is read lazily through a pipe, which connncts to its standard
output. In case the child replaces the process by calling an exec
variant,
it is make sure that the output is read from the new process' file descriptor
1.
lazy_pipe_from
calls hGetContents
, in order to read the pipe
lazily. This means that the file handle goes to semi-closed state. The handle
holds a file descriptor, and as long as the string isn't fully evaluated,
this file descriptor won't be closed. For the file descriptor to be closed,
first its standard output needs to be closed on the child side. This happens
when the child explicitly closes it, or the child process exits. When
afterwards the string on the parent side is completely evaluated, the handle,
along with the file descritor it holds, are closed and freed.
If you use the string in such a way that you only access the beginning of the string, the handle will remain in semi-closed state, holding a file descriptor, even when the pipe is closed on the child side. When you do that repeatedly, you may run out of file descriptors.
Unless you're sure that your program will reach the string's end, you should take care for it explicitly, by doing something like this:
(output, pid) <- lazy_pipe_from (exec "\/usr\/bin\/foobar" []) ... seq (length output) (return ())
This will read the entire standard output of the child, even if it isn't
needed. You can't cut the child process' output short, when you use
lazy_pipe_from
. If you need to do this, you should use h_pipe_from
, which
gives you the handle, which can then be closed by hClose
, even
if the child's output isn't completed:
(h, pid) <- h_pipe_from io -- Lazily read io's output output <- hGetContents h ... -- Not eveyting read yet, but cut io short. hClose h -- Wait for io to finish, and detect errors (Just ps) <- System.Posix.getProcessStatus True False pid when (ps /= Exited ExitSuccess) $ throw ps
When you close the handle before all data has been read, then the child gets
a SIGPIPE
signal.
After all the output has been read, you should call getProcessStatus
on the
child's process ID, in order to detect errors. Be aware that you must
evaluate the whole string, before calling getProcessStatus
blockingly, or
you'll get a deadlock.
You won't get an exception, if the child action exits in a way which
indicates an error. Errors occur asynchronously, when the output string is
evaluated. You must detect errors by yourself, by calling
getProcessStatus
.
In case the action doesn't replace the child process with an external
program, an exception may be thrown out of the action. This results in an error
message on stderr
, and the proper termination of the child. The
ProcessStatus
, which can be accessed in the parent process by
getProcessStatus
, is Exited (ExitFailure 1)
. The following exceptions are
understood, and result in corresponding messages: ArgError
,
ProcessStatus
, RunError
, IOError
and ExitCode
. Other exceptions
result in the generic message, as produced by show
.
Unless you replace the child process, calling an exec
variant, the child
should let the control flow leave the action normally. The child process is
then properly terminated, such that no resources, which have been duplicated
by the fork, cause problems. See HsShellScript for details.
Unlike shells' backquote feature, lazy_pipe_from
does not remove any trailing
newline characters. The entire output of the action is returned. You might want
to apply chomp
to the result.
The pipe is set to text mode. This means that the Unicode characters in the
text, which is read from the IO action's stdout, are converted from the system
character set to Unicode. The system character set is determined by the
environment variable LANG
. If you need to read binary data from the forked
process, you should use h_pipe_from and set the returned handle to binary mode.
This is accomplished by
. Then you can lazily read
the output of the action from the handle.hSetBinaryMode
h True
Example: Lazily read binary data from an IO action. Don't forget to collect
the child process later, using
.getProcessStatus
True False pid
(h, pid) <- h_pipe_from io hSetBinaryMode h True txt <- hGetContents h ... (Just ps) <- System.Posix.getProcessStatus True False pid
See exec
, pipe_to
, pipe_from
, h_pipe_from
, lazy_pipe_from2
, silently
.
:: IO a | Action 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 file handle.
This is like the backquote feature of shells.
This forks a subprocess, which executes the specified action. A file handle,
which is connected to its stdout
, is returned. The child's ProcessID
is returned as well. If the action replaces the child process, by calling an
exec
variant, it is made sure that its file descriptor 1 is connected to
the returned file handle.
This gives you full control of the pipe, and of the forked process. But you need to deal with the child process by yourself.
When you call getProcessStatus
blockingly, you must first ensure that all
data has been read, or close the handle. Otherwise you'll get a deadlock.
When you close the handle before all data has been read, then the child gets
a SIGPIPE
signal.
Unless you replace the child process, calling an exec
variant, the child
should let the control flow leave the action normally.
The child process is then properly terminated, such
that no resources, which have been duplicated by the fork, cause problems.
See HsShellScript for details.
Errors can only be detected by examining the child's process status (using
getProcessStatus
). No attempt is made to create more
meaningful exceptions, like it is done by runprog
/subproc
. If the child
action throws an exception, an error message is printed on stderr
, and the
child process exits with a ProcessStatus
of Exited (ExitFailure 1)
. The
following exceptions are understood, and result in corresponding messages:
ArgError
, ProcessStatus
, RunError
, IOError
and ExitCode
. Other
exceptions result in the generic message, as produced by show
.
The pipe is set to text mode. This means that the Unicode characters in the
text, which is read from stdin, is converted from the system character set to
Unicode. The system character set is determined by the environment variable
LANG
. If you need to read binary data from the forked process, you can set
the returned handle to binary mode. This is accomplished by
.hSetBinaryMode
h True
Example:
(h,pid) <- h_pipe_from $ exec "/usr/bin/foo" ["bar"]
See exec
, pipe_to
, h_pipe_from2
, pipe_from
, lazy_pipe_from
, chomp
, silently
.
See HsShellScript for more details.
Run an IO action as a separate process, and read its standard error output
strictly. Then wait for the child process to finish. This is like the
backquote feature of shells. This function is exactly the same as
pipe_from
, except that the standard error output is read, instead of the
standard output.
This forks a child process, which executes the specified action. The error output
of the child is read from its standard error output. In case it replaces the
process by calling an exec
variant, it is made sure that the output is
read from the new process' file descriptor 2.
The end of the child's error output is reached when either the standard error output is closed, or the child process exits. The program blocks until the action exits, even if the child closes its standard error output earlier. So the parent process always notices a failure of the action (which means it exits in a way which indicates an error).
When the child action exits in a way which indicates an error, the
corresponding ProcessStatus
is thrown. See
getProcessStatus
.
No attempt is made to create
more meaningful exceptions, like it is done by runprog
/subproc
.
Exceptions in the action result in an error message on stderr
, and the
proper termination of the child. This means that the error message is sent
through the pipe, to the parent process. The message can be found in the text
which has been read from the child process. It doesn't appear on the console.
The parent gets a ProcessStatus
exception, with
the value of Exited (ExitFailure 1)
. The following exceptions are
understood, and result in corresponding messages: ArgError
,
ProcessStatus
, RunError
, IOError
and ExitCode
. Other exceptions
result in the generic message, as produced by show
.
Unless you replace the child process, calling an exec
variant, the child
should let the control flow leave the action normally. The child process is
then properly terminated, such that no resources, which have been duplicated
by the fork, cause problems. See HsShellScript for details.
Unlike shells' backquote feature, pipe_from2
does not remove any trailing
newline characters. The entire error output of the action is returned. You might want
to apply chomp
to the result.
The pipe is set to text mode. This means that the Unicode characters in the
text, which is read from stdin, is converted from the system character set to
Unicode. The system character set is determined by the environment variable
LANG
. If you need to read binary data from the forked process, you should use
h_pipe_from
and set the returned handle to binary mode. This is
accomplished by
.hSetBinaryMode
h True
Example:
output <- pipe_from $ exec "/bin/mount" []
Example: Access both stdin
and stdout
of an external program.
import HsShellScript main = mainwrapper $ do res <- pipe_from $ pipe_to "2\n3\n1" $ exec "/usr/bin/sort" [] putStrLn res
See exec
, pipe_to
, pipe_from
, h_pipe_from2
, lazy_pipe_from2
, silently
.
See HsShellScript for more details.
:: IO a | Action 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 in a separate process, and read its standard error output, The output is read lazily, as the returned string is evaluated. The child's error output along with its process ID are returned.
This forks a child process, which executes the specified action. The error output
of the child is read lazily through a pipe, which connncts to its standard error
output. In case the child replaces the process by calling an exec
variant,
it is make sure that the output is read from the new process' file descriptor
1.
lazy_pipe_from
calls hGetContents
, in order to read the pipe
lazily. This means that the file handle goes to semi-closed state. The handle
holds a file descriptor, and as long as the string isn't fully evaluated,
this file descriptor won't be closed. For the file descriptor to be closed,
first its standard error output needs to be closed on the child side. This happens
when the child explicitly closes it, or the child process exits. When
afterwards the string on the parent side is completely evaluated, the handle,
along with the file descritor it holds, are closed and freed.
If you use the string in such a way that you only access the beginning of the string, the handle will remain in semi-closed state, holding a file descriptor, even when the pipe is closed on the child side. When you do that repeatedly, you may run out of file descriptors.
Unless you're sure that your program will reach the string's end, you should take care for it explicitly, by doing something like this:
(errmsg, pid) <- lazy_pipe_from2 (exec "/usr/bin/foobar" []) ... seq (length errmsg) (return ())
This will read the entire standard error output of the child, even if it isn't
needed. You can't cut the child process' output short, when you use
lazy_pipe_from
. If you need to do this, you should use h_pipe_from
, which
gives you the handle, which can then be closed by hClose
, even
if the child's output isn't completed:
(h, pid) <- h_pipe_from io -- Lazily read io's output output <- hGetContents h ... -- Not eveyting read yet, but cut io short. hClose h -- Wait for io to finish, and detect errors (Just ps) <- System.Posix.getProcessStatus True False pid when (ps /= Exited ExitSuccess) $ throw ps
When you close the handle before all data has been read, then the child gets
a SIGPIPE
signal.
After all the output has been read, you should call getProcessStatus
on the
child's process ID, in order to detect errors. Be aware that you must
evaluate the whole string, before calling getProcessStatus
blockingly, or
you'll get a deadlock.
You won't get an exception, if the child action exits in a way which
indicates an error. Errors occur asynchronously, when the output string is
evaluated. You must detect errors by yourself, by calling
getProcessStatus
.
In case the action doesn't replace the child process with an external
program, an exception may be thrown out of the action. This results in an
error message on stderr
. This means that the message is sent through the
pipe, to the parent process. Then the child process is properly terminated.
The ProcessStatus
, which can be accessed in the parent process by
getProcessStatus
, is Exited (ExitFailure 1)
. The following exceptions are
understood, and result in corresponding messages: ArgError
,
ProcessStatus
, RunError
, IOError
and ExitCode
. Other exceptions
result in the generic message, as produced by show
.
Unless you replace the child process, calling an exec
variant, the child
should let the control flow leave the action normally. The child process is
then properly terminated, such that no resources, which have been duplicated
by the fork, cause problems. See HsShellScript for details.
The pipe is set to text mode. This means that the Unicode characters in the
text, which is read from stdin, is converted from the system character set to
Unicode. The system character set is determined by the environment variable
LANG
. If you need to read binary data from the forked process, you can set
the returned handle to binary mode. This is accomplished by
.hSetBinaryMode
h True
Unlike shells' backquote feature, lazy_pipe_from
does not remove any trailing
newline characters. The entire output of the action is returned. You might want
to apply chomp
to the result.
The pipe is set to text mode. This means that the Unicode characters in the
text, which is read from the IO action's stdout, are converted from the
system character set to Unicode. The system character set is determined by
the environment variable LANG
. If you need to read binary data from the
forked process' standard error output, you should use h_pipe_from2 and set
the returned handle to binary mode. This is accomplished by
. Then you can lazily read the output of the action from the handle.hSetBinaryMode
h True
Example: Lazily read binary data from an IO action. Don't forget to collect
the child process later, using
.getProcessStatus
True False pid
(h, pid) <- h_pipe_from2 io hSetBinaryMode h True txt <- hGetContents h ... (Just ps) <- System.Posix.getProcessStatus True False pid
See exec
, pipe_to
, pipe_from2
, h_pipe_from2
, lazy_pipe_from
, silently
.
:: IO a | Action 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 file handle.
This forks a subprocess, which executes the specified action. A file handle,
which is connected to its stderr
, is returned. The child's ProcessID
is returned as well. If the action replaces the child process, by calling an
exec
variant, it is made sure that its file descriptor 2 is connected to
the returned file handle.
This gives you full control of the pipe, and of the forked process. But you need to deal with the child process by yourself.
When you call getProcessStatus
blockingly, you must first ensure that all
data has been read, or close the handle. Otherwise you'll get a deadlock.
When you close the handle before all data has been read, then the child gets
a SIGPIPE
signal.
Unless you replace the child process, calling an exec
variant, the child
should let the control flow leave the action normally. The child process is
then properly terminated, such that no resources, which have been duplicated
by the fork, cause problems. See HsShellScript for details.
Errors can only be detected by examining the child's process status (using
getProcessStatus
). No attempt is made to create more
meaningful exceptions, like it is done by runprog
/subproc
. If the child
action throws an exception, an error message is printed on stderr
. This
means that the message goes through the pipe to the parent process. Then the
child process exits with a ProcessStatus
of Exited (ExitFailure 1)
. The
following exceptions are understood, and result in corresponding messages:
ArgError
, ProcessStatus
, RunError
, IOError
and ExitCode
. Other
exceptions result in the generic message, as produced by show
.
The pipe is set to text mode. This means that the Unicode characters in the
text, which is read from stdin, is converted from the system character set to
Unicode. The system character set is determined by the environment variable
LANG
. If you need to read binary data from the forked process, you can set
the returned handle to binary mode. This is accomplished by
.hSetBinaryMode
h True
Example:
(h,pid) <- h_pipe_from $ exec "/usr/bin/foo" ["bar"]
See exec
, pipe_from
, pipe_from2
, h_pipe_from
, pipe_to
,
lazy_pipe_from
, chomp
, silently
.
:: IO a | Action to run as a separate process. Its return value is ignored. |
-> IO (String, ProcessStatus) | The output of the IO action until it terminated and the process status of the terminated action. |
Run an IO action as a separate process, and read its stdout
strictly. All
the output is read, until the action terminates. Contrary to pipe_from
,
when the action signals an error (with a non-zero exit code), the error isn't
thrown as an exception, but is returned alongside the output.
The result consists of the output which has been read, alongside with the
process status, with which the action has terminated. For success,
the process status is Exited ExitSuccess
. See
ProcessStatus
.
This is a frontend to the h_pipe_from
function. See there for more details.
See pipe_from_full2
, exec
, pipe_to
, pipe_from
, h_pipe_from
, lazy_pipe_from
, chomp
.
:: IO a | Action to run as a separate process. Its return value is ignored. |
-> IO (String, ProcessStatus) | The error output of the IO action until it terminated and the process status of the terminated action. |
Run an IO action as a separate process, and read its stderr
strictly. All
the error output is read, until the action terminates. Contrary to pipe_from2
,
when the action signals an error (with a non-zero exit code), the error isn't
thrown as an exception, but is returned alongside the output.
The result consists of the error output which has been read, alongside with
the process status, with which the action has terminated. For success, the
process status is Exited ExitSuccess
. See
ProcessStatus
.
This is a frontend to the h_pipe_from2
function. See there for more details.
See pipe_from_full
, exec
, pipe_to
, pipe_from
, h_pipe_from
, lazy_pipe_from
,
chomp
.
:: 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 |
Run an IO action as a separate process, and optionally connect to its
stdin
, its stdout
and its stderr
output with pipes.
This forks a subprocess, which executes the specified action. The child's
ProcessID
is returned. Some of the action's standard handles are made to
connected to pipes, which the caller can use in order to communicate with the
new child process. Which, this is determined by the first three arguments.
You get full control of the pipes, and of the forked process. But you need to deal with the child process by yourself.
Errors in the child process can only be detected by examining its process
status (using getProcessStatus
). If the child action
throws an exception, an error message is printed on stderr
, and the child
process exits with a ProcessStatus
of Exited (ExitFailure 1)
. The
following exceptions are understood, and result in corresponding messages:
ArgError
, ProcessStatus
, RunError
, IOError
and ExitCode
. Other
exceptions result in the generic message, as produced by show
.
Unless you replace the child process, calling an exec
variant, the child
should let the control flow leave the action normally. It is then properly
take care of.
The pipes are set to text mode. When connecting to the child's stdin
,
this means that the Unicode characters in the Haskell side text are converted
to the system character set. When reading from the child's stdout
or
stderr
, the text is converted from the system character set to Unicode in
the Haskell-side strings. The system character set is determined by the
environment variable LANG
. If you need to read or write binary data, then
this is no problem. Just call
. This sets the
corresponding pipe to binary mode.hSetBinaryMode
handle True
See pipe_from
, h_pipe_from
, pipe_from2
, h_pipe_from2
, pipe_to
,
h_pipe_to
, lazy_pipe_from
, lazy_pipe_from2
Shell-like Quoting
module HsShellScript.Shell
Creating temporary files and directories
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
.
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
.
:: 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
.
:: 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
.
:: 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
.
:: 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.
See tmp_file
, with_temp_file
, 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_temp_dir
doesn't delete any
files inside, so the directory could be removed. If the directory isn't empty, an IOError
results (with the
path filled in). When the action throws an exception, and the temporary directory cannot be removed, then the
exception is passed through, rather than replacing it with the IOError. (This is because it's probably exactly
because of that exception that the directory isn't empty and can't be removed).
with_tmp_dir prefix io = with_temp_dir 6 (prefix ++ ".") "" io
See tmp_dir
, with_temp_dir
, with_tmp_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.
See temp_file
, with_tmp_file
, 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 removed. If the directory isn't empty, an IOError
results (with the
path filled in). When the action throws an exception, and the temporary directory cannot be removed, then the
exception is passed through, rather than replacing it with the IOError. (This is because it's probably exactly
because of that exception that the directory isn't empty and can't be removed).
See temp_dir
, with_tmp_dir
, with_temp_file
.
Reading mount information
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.
See read_mounts
, read_mtab
, read_fstab
.
:: String | File to read (typically |
-> 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_fstab :: IO [Mntent] Source #
Output to the standard stream, colorful logging and error reporting
Print text to stdout
.
This is a shorthand for putStrLn
, except for stderr
being flushed
beforehand. This way normal output and error output appear in
order, even when they aren't buffered as by default.
An additional newline is printed at the end.
outm msg = do hFlush stderr putStrLn msg
Print text to stdout
.
This is a shorthand for putStr
, except for stderr
being flushed
beforehand. This way normal output and error output appear in
order, even when they aren't buffered as by default.
No newline is printed at the end.
outm_ msg = do hFlush stderr putStr msg
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.
stdout
is flushed beforehand. So normal output and error output appear in
order, even when they aren't buffered as by default.
logm_ :: String -> IO () Source #
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.
stdout
is flushed beforehand. So normal output and error output appear in
order, even when they aren't buffered as by default.
errm :: String -> IO () Source #
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.
stdout
is flushed beforehand. So normal output and error output appear in
order, even when they aren't buffered as by default.
errm_ :: String -> IO () Source #
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.
stdout
is flushed beforehand. So normal output and error output appear in
order, even when they aren't buffered as by default.
Check if a handle is connected to a terminal.
This is a front end to the isatty(3)
function (see man page). It is useful,
for instance, to determine if color escape sequences should be
generated.
:: Handle | Handle, which is connected to the terminal |
-> IO (Maybe Int) | The number of columns in the constrolling terminal. Nothing, when the handle isn't connected to a terminal. |
Determine the terminal width in columns.
This value can be used to format output to fit the terminal.
This queries the terminal which is connected to stdout. It may happen, that
stdout isn't connected to a terminal, for instance when the program is part of
a pipe. In this case, Nothing
is returned. No exception is thrown.
See terminal_width_ioe
, make_usage_info
, print_usage_info
, usage_info
, wrap
.
:: Handle | Handle, which is connected to the terminal |
-> IO Int | The number of columns in the constrolling terminal. Throws an IOError when the handle isn't connected to a terminal. |
Determine the terminal width in columns.
This value can be used to format output to fit the terminal.
This queries the terminal which is connected to stdout. It may happen, that stdout isn't connected to a terminal. For instance when the program is part of a pipe. In this case, an IOError is thrown.
See terminal_width
, make_usage_info
, print_usage_info
, usage_info
, wrap
.
Miscellaneous
:: 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. If the string representation of the Inŧ
is longer than the number of
characters to fill up, this produces as many characters as needed.
Remove trailing newlines. This is silimar to perl's chomp
procedure.
:: String | Either the name of a file, or |
-> IO String | The lazily read contents of the file or |
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
:: String | either the name of a file, or |
-> 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.
This is an interface to the POSIX glob
function, which does wildcard expansion
in paths. The sorted list of matched paths is returned. It's empty
for no match (rather than the original pattern). In case anything goes wrong
(such as permission denied), an IOError is thrown.
This does not do tilde expansion, which is done (among many unwanted other
things) by wordexp
. The only flag used for the call to glob
is GLOB_ERR
.
The behaviour in case of non-existing path components is inconsistent in the
GNU version of the underlying glob
function. glob "/doesnt_exist/foo"
will return
the empty list, whereas glob "/doesnt_exist/*"
causes a "No such file or directory"
IOError.
Note that it isn't clear if dangling symlinks are matched by glob. From the web: "Compared to other glob implementation (*BSD, bash, musl, and other shells as well), GLIBC seems the be the only one that does not match dangling symlinks. ... POSIX does not have any strict specification for dangling symlinks".
You will have to work around this problem, probably using System.Directory.getDirectoryContents.
See man pages glob(3)
and wordexp(3)
.
glob_quote :: String -> String Source #
Quote special characters for use with the glob
function.
The characters *
, ?
, [
and \
must be quoted by preceding
backslashs, when they souldn't have their special meaning. The glob_quote
function does this.
You can't use quote
or shell_quote
.
See glob
, quote
, shell_quote
Error Handling
Error reporting wrapper for the main
function. This catches any
HsShellScript generated exceptions, and IOError
s, prints
an error message and exits with exitFailure
. The main
function
typically looks like this:
main = mainwrapper $ do ...
The exceptions caught are ArgError
, RunError
, ProcessStatus
and IOError
.
Read the global system error number. This is the POSIX errno
value. This
function is redundant. Use Foreign.C.Error.getErrno
instead.
Generate an error message from an errno
value. This is the POSIX
strerror
system library function.
See the man page strerror(3)
.
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)
.
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)
.
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 Source #
Print a message to stderr
and exit with an exit code
indicating an error.
failIO msg = hPutStrLn stderr msg >> exitFailure
Modify an IO action to return the exit code of a failed program call, instead of throwing an exception.
This is used to modify the error reporting behaviour of an IO action which
uses run
runprog
or call
subproc
. When an external program exits with
an exit code which indicates an error, normally an exception is thrown. After
exitcode
has been applied, the exit code is retruned instead.
The caught exceptions are RunError
and ProcessStatus
. Termination by a
signal is still reported by an exception, which is passed through.
Example: ec <- exitcode $ runprog "foo" ["bar"]
:: 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 Source #
Convert an IOError
to a string.
There is an instance declaration of IOError
in Show
in the GHC.IO
library, but show_ioerror
produces a
more readable, and more complete, message.
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
.
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
.
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 -- ...