-- Hoogle documentation, generated by Haddock -- See Hoogle, http://www.haskell.org/hoogle/ -- | Use OS processes as stream transformation functions -- -- Use operating system (OS) commands in Haskell programs as if they were -- native Haskell functions, by treating their inputs and outputs as -- Haskell streams. This allows you to write high-level Haskell scripts -- that can perform tasks similar to shell scripts, but with C-like -- performance, and with strong safety guarantees, refactorability, and -- modularity. @package streamly-process @version 0.3.1 module Streamly.Internal.System.Process -- | Process configuration used for creating a new process. -- -- By default the process config is setup to inherit the following -- attributes from the parent process: -- -- -- -- On POSIX: -- -- -- -- On Windows by default the parent process waits for the entire child -- process tree to finish. data Config -- | Set the current working directory of the new process. When -- Nothing, the working directory is inherited from the parent -- process. -- -- Default is Nothing - inherited from the parent process. setCwd :: Maybe FilePath -> Config -> Config -- | Set the environment variables for the new process. When -- Nothing, the environment is inherited from the parent process. -- -- Default is Nothing - inherited from the parent process. setEnv :: Maybe [(String, String)] -> Config -> Config -- | Close all open file descriptors inherited from the parent process. -- Note, this does not apply to stdio descriptors - the behavior of those -- is determined by other configuration settings. -- -- Default is False. -- -- Note: if the number of open descriptors is large, it may take a while -- closing them. closeFiles :: Bool -> Config -> Config -- | If True the new process starts a new process group, becomes a -- process group leader, its pid becoming the process group id. -- -- See the POSIX setpgid man page. -- -- Default is False, the new process belongs to the parent's -- process group. newProcessGroup :: Bool -> Config -> Config -- | InheritSession makes the new process inherit the terminal -- session from the parent process. This is the default. -- -- NewSession makes the new process start with a new session -- without a controlling terminal. On POSIX, setsid is used to -- create a new process group and session, the pid of the new process is -- the session id and process group id as well. On Windows -- DETACHED_PROCESS flag is used to detach the process from -- inherited console session. -- -- NewConsole creates a new terminal and attaches the process to -- the new terminal on Windows, using the CREATE_NEW_CONSOLE flag. On -- POSIX this does nothing. -- -- For Windows see -- -- -- -- For POSIX see, setsid man page. data Session -- | Inherit the parent session InheritSession :: Session -- | Detach process from the current session NewSession :: Session -- | Windows only, CREATE_NEW_CONSOLE flag NewConsole :: Session -- | Define the terminal session behavior for the new process. -- -- Default is InheritSession. setSession :: Session -> Config -> Config -- | When this is True, the parent process ignores user interrupt -- signals SIGINT and SIGQUIT delivered to it until the -- child process exits. If multiple child processes are started then the -- default handling in the parent is restored only after the last one -- exits. -- -- When a user presses CTRL-C or CTRL- on the terminal, a SIGINT or -- SIGQUIT is sent to all the foreground processes in the terminal -- session, this includes both the child and the parent. By default, on -- receiving these signals, the parent process would cleanup and exit, to -- avoid that and let the child handle these signals we can choose to -- ignore these signals in the parent until the child exits. -- -- POSIX only. Default is False. interruptChildOnly :: Bool -> Config -> Config -- | Use the POSIX setuid call to set the user id of the new process -- before executing the command. The parent process must have sufficient -- privileges to set the user id. -- -- POSIX only. See the POSIX setuid man page. -- -- Default is Nothing - inherit from the parent. setUserId :: Maybe Word32 -> Config -> Config -- | Use the POSIX setgid call to set the group id of the new -- process before executing the command. The parent process must have -- sufficient privileges to set the group id. -- -- POSIX only. See the POSIX setgid man page. -- -- Default is Nothing - inherit from the parent. setGroupId :: Maybe Word32 -> Config -> Config -- | On Windows, the parent waits for the entire descendant tree of process -- i.e. including processes that are spawned by the child process. -- -- Default is True. waitForDescendants :: Bool -> Config -> Config inheritStdin :: Config -> Config inheritStdout :: Config -> Config pipeStdErr :: Config -> Config -- | An exception that is raised when a process fails. newtype ProcessFailure -- | The exit code of the process. ProcessFailure :: Int -> ProcessFailure -- | The following code is equivalent to the shell command echo "hello -- world": -- --
--   >>> :{
--      Process.toBytes "echo" ["hello world"]
--    & Stream.fold Stdio.write
--    :}
--   hello world
--   
toBytes :: (MonadAsync m, MonadCatch m) => FilePath -> [String] -> Stream m Word8 -- | The following code is equivalent to the shell command echo "hello -- world": -- --
--   >>> :{
--      Process.toChunks "echo" ["hello world"]
--    & Stream.fold Stdio.writeChunks
--    :}
--   hello world
--   
-- --
--   >>> toChunks = toChunksWith id
--   
toChunks :: (MonadAsync m, MonadCatch m) => FilePath -> [String] -> Stream m (Array Word8) -- | Like toChunks but use the specified configuration to run the -- process. toChunksWith :: (MonadCatch m, MonadAsync m) => (Config -> Config) -> FilePath -> [String] -> Stream m (Array Word8) -- |
--   >>> toChars path args = toBytes path args & Unicode.decodeUtf8
--   
toChars :: (MonadAsync m, MonadCatch m) => FilePath -> [String] -> Stream m Char -- |
--   >>> toLines path args f = toChars path args & Unicode.lines f
--   
toLines :: (MonadAsync m, MonadCatch m) => Fold m Char a -> FilePath -> [String] -> Stream m a -- |
--   >>> toString path args = toChars path args & Stream.fold Fold.toList
--   
toString :: (MonadAsync m, MonadCatch m) => FilePath -> [String] -> m String -- |
--   >>> toStdout path args = toChunks path args & Stdio.putChunks
--   
toStdout :: (MonadAsync m, MonadCatch m) => FilePath -> [String] -> m () -- |
--   >>> toNull path args = toChunks path args & Stream.fold Fold.drain
--   
toNull :: (MonadAsync m, MonadCatch m) => FilePath -> [String] -> m () -- | Like pipeChunks except that it works on a stream of bytes -- instead of a stream of chunks. -- -- We can write the example in pipeChunks as follows. -- --
--   >>> :{
--      Process.toBytes "echo" ["hello world"]
--    & Process.pipeBytes "tr" ["[a-z]", "[A-Z]"]
--    & Stream.fold Stdio.write
--    :}
--   HELLO WORLD
--   
-- -- pre-release pipeBytes :: (MonadCatch m, MonadAsync m) => FilePath -> [String] -> Stream m Word8 -> Stream m Word8 -- | pipeChunks file args input runs the executable file -- specified by its name or path using args as arguments and -- input stream as its standard input. Returns the standard -- output of the executable as a stream. -- -- If only the name of an executable file is specified instead of its -- path then the file name is searched in the directories specified by -- the PATH environment variable. -- -- If the input stream throws an exception or if the output stream is -- garbage collected before it could finish then the process is -- terminated with SIGTERM. -- -- If the process terminates with a non-zero exit code then a -- ProcessFailure exception is raised. -- -- The following code is equivalent to the shell command echo "hello -- world" | tr [a-z] [A-Z]: -- --
--   >>> :{
--      Process.toChunks "echo" ["hello world"]
--    & Process.pipeChunks "tr" ["[a-z]", "[A-Z]"]
--    & Stream.fold Stdio.writeChunks
--    :}
--   HELLO WORLD
--   
-- -- pre-release pipeChunks :: (MonadCatch m, MonadAsync m) => FilePath -> [String] -> Stream m (Array Word8) -> Stream m (Array Word8) -- | Like pipeChunks but use the specified configuration to run the -- process. pipeChunksWith :: (MonadCatch m, MonadAsync m) => (Config -> Config) -> FilePath -> [String] -> Stream m (Array Word8) -> Stream m (Array Word8) -- | Like pipeChunks except that it works on a stream of chars -- instead of a stream of chunks. -- --
--   >>> :{
--      Process.toChars "echo" ["hello world"]
--    & Process.pipeChars "tr" ["[a-z]", "[A-Z]"]
--    & Stdio.putChars
--    :}
--   HELLO WORLD
--   
-- -- We can seamlessly replace the tr process with the Haskell -- toUpper function: -- --
--   >>> :{
--      Process.toChars "echo" ["hello world"]
--    & fmap toUpper
--    & Stdio.putChars
--    :}
--   HELLO WORLD
--   
-- -- pre-release pipeChars :: (MonadCatch m, MonadAsync m) => FilePath -> [String] -> Stream m Char -> Stream m Char -- | toBytesEither path args runs the executable at path -- using args as arguments and returns a stream of Either -- bytes. The Left values are from stderr and the -- Right values are from stdout of the executable. -- -- Raises ProcessFailure exception in case of failure. -- -- The following example uses echo to write hello to -- stdout and world to stderr, then uses folds -- from Streamly.Console.Stdio to write them back to -- stdout and stderr respectively: -- --
--   >>> :{
--     Process.toBytesEither "/bin/bash" ["-c", "echo 'hello'; echo 'world' 1>&2"]
--   & Stream.fold (Fold.partition Stdio.writeErr Stdio.write)
--   :}
--   world
--   hello
--   ((),())
--   
toBytesEither :: (MonadAsync m, MonadCatch m) => FilePath -> [String] -> Stream m (Either Word8 Word8) -- | Like toBytesEither but generates a stream of Array -- Word8 instead of a stream of Word8. -- --
--   >>> :{
--     toChunksEither "bash" ["-c", "echo 'hello'; echo 'world' 1>&2"]
--   & Stream.fold (Fold.partition Stdio.writeErrChunks Stdio.writeChunks)
--   :}
--   world
--   hello
--   ((),())
--   
-- --
--   >>> toChunksEither = toChunksEitherWith id
--   
-- -- Prefer toChunksEither over toBytesEither when -- performance matters. -- -- Pre-release toChunksEither :: (MonadAsync m, MonadCatch m) => FilePath -> [String] -> Stream m (Either (Array Word8) (Array Word8)) -- | Like toChunksEither but use the specified configuration to run -- the process. toChunksEitherWith :: (MonadCatch m, MonadAsync m) => (Config -> Config) -> FilePath -> [String] -> Stream m (Either (Array Word8) (Array Word8)) -- | pipeBytesEither path args input runs the executable at -- path using args as arguments and input -- stream as its standard input. The error stream of the executable is -- presented as Left values in the resulting stream and output -- stream as Right values. -- -- Raises ProcessFailure exception in case of failure. -- -- For example, the following is equivalent to the shell command echo -- "hello world" | tr [:lower:] [:upper:]: -- --
--   >>> :{
--      pipeBytesEither "echo" ["hello world"] Stream.nil
--    & Stream.catRights
--    & pipeBytesEither "tr" ["[:lower:]", "[:upper:]"]
--    & Stream.catRights
--    & Stream.fold Stdio.write
--    :}
--   HELLO WORLD
--   
pipeBytesEither :: (MonadCatch m, MonadAsync m) => FilePath -> [String] -> Stream m Word8 -> Stream m (Either Word8 Word8) -- | Like pipeChunks but also includes stderr as Left stream -- in the Either output. pipeChunksEither :: (MonadCatch m, MonadAsync m) => FilePath -> [String] -> Stream m (Array Word8) -> Stream m (Either (Array Word8) (Array Word8)) -- | Like pipeChunksEither but use the specified configuration to -- run the process. pipeChunksEitherWith :: (MonadCatch m, MonadAsync m) => (Config -> Config) -> FilePath -> [String] -> Stream m (Array Word8) -> Stream m (Either (Array Word8) (Array Word8)) -- | Launch a process interfacing with the user. User interrupts are sent -- to the launched process and ignored by the parent process. The -- launched process inherits stdin, stdout, and stderr from the parent, -- so that the user can interact with the process. The parent waits for -- the child process to exit, an ExitCode is returned when the -- process finishes. -- -- This is the same as the common system function found in other -- libraries used to execute commands. -- -- On Windows you can pass setSession NewConsole to create a new -- console. foreground :: (Config -> Config) -> FilePath -> [String] -> IO ExitCode -- | Launch a daemon process. Closes stdin, stdout and stderr, creates a -- new session, detached from the terminal, the parent does not wait for -- the process to finish. -- -- The ProcessHandle returned can be used to terminate the daemon -- or send signals to it. daemon :: (Config -> Config) -> FilePath -> [String] -> IO ProcessHandle -- | Launch a standalone process i.e. the process does not have a way to -- attach the IO streams with other processes. The IO streams stdin, -- stdout, stderr can either be inherited from the parent or closed. -- -- This API is more powerful than interactive and daemon -- and can be used to implement both of these. However, it should be used -- carefully e.g. if you inherit the IO streams and parent is not waiting -- for the child process to finish then both parent and child may use the -- IO streams resulting in garbled IO if both are reading/writing -- simultaneously. -- -- If the parent chooses to wait for the process an ExitCode is -- returned otherwise a ProcessHandle is returned which can be -- used to terminate the process, send signals to it or wait for it to -- finish. standalone :: Bool -> (Bool, Bool, Bool) -> (Config -> Config) -> FilePath -> [String] -> IO (Either ExitCode ProcessHandle) -- | Deprecated: Use interruptChildOnly instead. parentIgnoresInterrupt :: Bool -> Config -> Config -- | Deprecated: Use waitForDescendants instead. waitForChildTree :: Bool -> Config -> Config -- | Deprecated: Use foreground instead. interactive :: (Config -> Config) -> FilePath -> [String] -> IO ExitCode -- | Deprecated: Please use pipeBytes instead. processBytes :: (MonadCatch m, MonadAsync m) => FilePath -> [String] -> Stream m Word8 -> Stream m Word8 -- | Deprecated: Please use pipeChunks instead. processChunks :: (MonadCatch m, MonadAsync m) => FilePath -> [String] -> Stream m (Array Word8) -> Stream m (Array Word8) instance GHC.Show.Show Streamly.Internal.System.Process.ProcessFailure instance GHC.Exception.Type.Exception Streamly.Internal.System.Process.ProcessFailure module Streamly.Internal.System.Command -- |
--   >>> toBytes "echo hello world" & Stdio.putBytes
--   hello world
--   
--   >>> toBytes "echo hello\\ world" & Stdio.putBytes
--   hello world
--   
--   >>> toBytes "echo 'hello world'" & Stdio.putBytes
--   hello world
--   
--   >>> toBytes "echo \"hello world\"" & Stdio.putBytes
--   hello world
--   
-- -- Pre-release toBytes :: (MonadAsync m, MonadCatch m) => String -> Stream m Word8 -- |
--   >>> toChunks "echo hello world" & Stdio.putChunks
--   hello world
--   
-- -- Pre-release toChunks :: (MonadAsync m, MonadCatch m) => String -> Stream m (Array Word8) -- | Like toChunks but use the specified configuration to run the -- process. toChunksWith :: (MonadCatch m, MonadAsync m) => (Config -> Config) -> String -> Stream m (Array Word8) -- |
--   >>> toChars "echo hello world" & Stdio.putChars
--   hello world
--   
-- -- Pre-release toChars :: (MonadAsync m, MonadCatch m) => String -> Stream m Char -- |
--   >>> toLines Fold.toList "echo -e hello\\\\nworld" & Stream.fold Fold.toList
--   ["hello","world"]
--   
-- -- Pre-release toLines :: (MonadAsync m, MonadCatch m) => Fold m Char a -> String -> Stream m a -- |
--   >>> toString "echo hello world"
--   "hello world\n"
--   
-- -- Pre-release toString :: (MonadAsync m, MonadCatch m) => String -> m String -- |
--   >>> toStdout "echo hello world"
--   hello world
--   
-- -- Pre-release toStdout :: (MonadAsync m, MonadCatch m) => String -> m () -- |
--   >>> toNull "echo hello world"
--   
-- -- Pre-release toNull :: (MonadAsync m, MonadCatch m) => String -> m () -- | Like pipeChunks except that it works on a stream of bytes -- instead of a stream of chunks. -- --
--   >>> :{
--      toBytes "echo hello world"
--    & pipeBytes "tr [a-z] [A-Z]"
--    & Stdio.putBytes
--    :}
--   HELLO WORLD
--   
-- -- Pre-release pipeBytes :: (MonadAsync m, MonadCatch m) => String -> Stream m Word8 -> Stream m Word8 -- | Like pipeChunks except that it works on a stream of chars -- instead of a stream of chunks. -- --
--   >>> :{
--      toChars "echo hello world"
--    & pipeChars "tr [a-z] [A-Z]"
--    & Stdio.putChars
--    :}
--   HELLO WORLD
--   
-- -- Pre-release pipeChars :: (MonadAsync m, MonadCatch m) => String -> Stream m Char -> Stream m Char -- | pipeChunks command input runs the executable with arguments -- specified by command and supplying input stream as -- its standard input. Returns the standard output of the executable as a -- stream of byte arrays. -- -- If only the name of an executable file is specified instead of its -- path then the file name is searched in the directories specified by -- the PATH environment variable. -- -- If the input stream throws an exception or if the output stream is -- garbage collected before it could finish then the process is -- terminated with SIGTERM. -- -- If the process terminates with a non-zero exit code then a -- ProcessFailure exception is raised. -- -- The following code is equivalent to the shell command echo "hello -- world" | tr [a-z] [A-Z]: -- --
--   >>> :{
--      toChunks "echo hello world"
--    & pipeChunks "tr [a-z] [A-Z]"
--    & Stdio.putChunks
--    :}
--   HELLO WORLD
--   
-- -- Pre-release pipeChunks :: (MonadAsync m, MonadCatch m) => String -> Stream m (Array Word8) -> Stream m (Array Word8) -- | Like pipeChunks but use the specified configuration to run the -- process. pipeChunksWith :: (MonadCatch m, MonadAsync m) => (Config -> Config) -> String -> Stream m (Array Word8) -> Stream m (Array Word8) -- | Launch a standlone process i.e. the process does not have a way to -- attach the IO streams with other processes. The IO streams stdin, -- stdout, stderr can either be inherited from the parent or closed. -- -- This API is more powerful than interactive and daemon -- and can be used to implement both of these. However, it should be used -- carefully e.g. if you inherit the IO streams and parent is not waiting -- for the child process to finish then both parent and child may use the -- IO streams resulting in garbled IO if both are reading/writing -- simultaneously. -- -- If the parent chooses to wait for the process an ExitCode is -- returned otherwise a ProcessHandle is returned which can be -- used to terminate the process, send signals to it or wait for it to -- finish. standalone :: Bool -> (Bool, Bool, Bool) -> (Config -> Config) -> String -> IO (Either ExitCode ProcessHandle) -- | Launch a process interfacing with the user. User interrupts are sent -- to the launched process and ignored by the parent process. The -- launched process inherits stdin, stdout, and stderr from the parent, -- so that the user can interact with the process. The parent waits for -- the child process to exit, an ExitCode is returned when the -- process finishes. -- -- This is the same as the common system function found in other -- libraries used to execute commands. -- -- On Windows you can pass setSession NewConsole to create a new -- console. foreground :: (Config -> Config) -> String -> IO ExitCode -- | Launch a daemon process. Closes stdin, stdout and stderr, creates a -- new session, detached from the terminal, the parent does not wait for -- the process to finish. -- -- The ProcessHandle returned can be used to terminate the daemon -- or send signals to it. daemon :: (Config -> Config) -> String -> IO ProcessHandle quotedWord :: MonadCatch m => Parser Char m String -- | A modifier for process running APIs in Streamly.System.Process -- to run command strings. -- -- For example: -- --
--   >>> runWith Process.toString "echo hello"
--   "hello\n"
--   
--   >>> runWith Process.toStdout "echo hello"
--   hello
--   
-- -- Internal runWith :: MonadCatch m => (FilePath -> [String] -> m a) -> String -> m a -- | A modifier for stream generation APIs in -- Streamly.System.Process to generate streams from command -- strings. -- -- For example: -- --
--   >>> streamWith Process.toBytes "echo hello" & Stdio.putBytes
--   hello
--   
--   >>> streamWith Process.toChunks "echo hello" & Stdio.putChunks
--   hello
--   
-- -- Internal streamWith :: MonadCatch m => (FilePath -> [String] -> Stream m a) -> String -> Stream m a -- | A modifier for process piping APIs in Streamly.System.Process -- to pipe data through processes specified by command strings. -- -- For example: -- --
--   >>> :{
--      toChunks "echo hello"
--    & pipeWith Process.pipeChunks "tr [a-z] [A-Z]"
--    & Stdio.putChunks
--    :}
--   HELLO
--   
-- -- Internal pipeWith :: MonadCatch m => (FilePath -> [String] -> Stream m a -> Stream m b) -> String -> Stream m a -> Stream m b -- | This module provides a way to invoke external executables and use them -- seamlessly in a Haskell program, in a streaming fashion. This enables -- you to write high-level Haskell scripts to perform tasks similar to -- shell scripts without requiring the shell. Moreover, Haskell scripts -- provide C-like performance. -- -- Please see the Streamly.System.Process for basics. This module -- is a wrapper over that module. Streamly.System.Process requires -- specifying a command executable name and its arguments separately -- (e.g. "ls" "-al") whereas using this module we can specify the -- executable and its arguments more conveniently as a single command -- string e.g. we can execute "ls -al". -- -- A command string is parsed in the same way as a posix shell would -- parse it. A command string consists of whitespace separated tokens -- with the first token treated as the executable name and the rest as -- arguments. Whitespace can be escaped using \. Alternatively, -- double quotes or single quotes can be used to enclose tokens with -- whitespaces. Quotes can be escaped using \. Single quotes -- inside double quotes or vice-versa are treated as normal characters. -- -- You can use the string quasiquoter str to write commands -- conveniently, it allows Haskell variable expansion as well e.g.: -- --
--   >>> f = "file name"
--   
--   >>> [str|ls -al "#{f} with spaces"|]
--   "ls -al \"file name with spaces\""
--   
-- -- With the Streamly.System.Command module you can write the -- examples in the Streamly.System.Process module more -- conveniently. -- --

Executables as functions

-- -- The shell command echo "hello world" | tr [a-z] [A-Z] can be -- written as follows using this module: -- --
--   >>> :{
--      Command.toBytes [str|echo "hello world"|]
--    & Command.pipeBytes [str|tr [a-z] [A-Z]|]
--    & Stream.fold Stdio.write
--    :}
--    HELLO WORLD
--   
-- --

Shell commands as functions

-- -- We recommend using streamly to compose commands natively in Haskell -- rather than using the shell as shown in the previous example. However, -- if for some reason you want to execute commands using the shell: -- --
--   >>> :{
--      Command.toBytes [str|sh "-c" "echo 'hello world' | tr [a-z] [A-Z]"|]
--    & Stream.fold Stdio.write
--    :}
--    HELLO WORLD
--   
-- --

Running Commands Concurrently

-- -- This example shows the power of composing in Haskell rather than using -- the shell. Running grep concurrently on many files: -- --
--   >>> :{
--   grep file =
--      Command.toBytes [str|grep -H "pattern" #{file}|]
--    & Stream.handle (\(_ :: Command.ProcessFailure) -> Stream.nil)
--    & Stream.foldMany (Fold.takeEndBy (== 10) Array.write)
--    :}
--   
-- --
--   >>> :{
--   pgrep =
--      Dir.readFiles "."
--    & Stream.parConcatMap id grep
--    & Stream.fold Stdio.writeChunks
--   :}
--   
-- --

Experimental APIs

-- -- See Streamly.Internal.System.Command for unreleased APIs. module Streamly.System.Command -- | An exception that is raised when a process fails. newtype ProcessFailure -- | The exit code of the process. ProcessFailure :: Int -> ProcessFailure -- | Process configuration used for creating a new process. -- -- By default the process config is setup to inherit the following -- attributes from the parent process: -- -- -- -- On POSIX: -- -- -- -- On Windows by default the parent process waits for the entire child -- process tree to finish. data Config -- | Set the current working directory of the new process. When -- Nothing, the working directory is inherited from the parent -- process. -- -- Default is Nothing - inherited from the parent process. setCwd :: Maybe FilePath -> Config -> Config -- | Set the environment variables for the new process. When -- Nothing, the environment is inherited from the parent process. -- -- Default is Nothing - inherited from the parent process. setEnv :: Maybe [(String, String)] -> Config -> Config -- | Close all open file descriptors inherited from the parent process. -- Note, this does not apply to stdio descriptors - the behavior of those -- is determined by other configuration settings. -- -- Default is False. -- -- Note: if the number of open descriptors is large, it may take a while -- closing them. closeFiles :: Bool -> Config -> Config -- | If True the new process starts a new process group, becomes a -- process group leader, its pid becoming the process group id. -- -- See the POSIX setpgid man page. -- -- Default is False, the new process belongs to the parent's -- process group. newProcessGroup :: Bool -> Config -> Config -- | InheritSession makes the new process inherit the terminal -- session from the parent process. This is the default. -- -- NewSession makes the new process start with a new session -- without a controlling terminal. On POSIX, setsid is used to -- create a new process group and session, the pid of the new process is -- the session id and process group id as well. On Windows -- DETACHED_PROCESS flag is used to detach the process from -- inherited console session. -- -- NewConsole creates a new terminal and attaches the process to -- the new terminal on Windows, using the CREATE_NEW_CONSOLE flag. On -- POSIX this does nothing. -- -- For Windows see -- -- -- -- For POSIX see, setsid man page. data Session -- | Inherit the parent session InheritSession :: Session -- | Detach process from the current session NewSession :: Session -- | Windows only, CREATE_NEW_CONSOLE flag NewConsole :: Session -- | Define the terminal session behavior for the new process. -- -- Default is InheritSession. setSession :: Session -> Config -> Config -- | When this is True, the parent process ignores user interrupt -- signals SIGINT and SIGQUIT delivered to it until the -- child process exits. If multiple child processes are started then the -- default handling in the parent is restored only after the last one -- exits. -- -- When a user presses CTRL-C or CTRL- on the terminal, a SIGINT or -- SIGQUIT is sent to all the foreground processes in the terminal -- session, this includes both the child and the parent. By default, on -- receiving these signals, the parent process would cleanup and exit, to -- avoid that and let the child handle these signals we can choose to -- ignore these signals in the parent until the child exits. -- -- POSIX only. Default is False. interruptChildOnly :: Bool -> Config -> Config -- | Use the POSIX setuid call to set the user id of the new process -- before executing the command. The parent process must have sufficient -- privileges to set the user id. -- -- POSIX only. See the POSIX setuid man page. -- -- Default is Nothing - inherit from the parent. setUserId :: Maybe Word32 -> Config -> Config -- | Use the POSIX setgid call to set the group id of the new -- process before executing the command. The parent process must have -- sufficient privileges to set the group id. -- -- POSIX only. See the POSIX setgid man page. -- -- Default is Nothing - inherit from the parent. setGroupId :: Maybe Word32 -> Config -> Config -- | On Windows, the parent waits for the entire descendant tree of process -- i.e. including processes that are spawned by the child process. -- -- Default is True. waitForDescendants :: Bool -> Config -> Config -- |
--   >>> toBytes "echo hello world" & Stdio.putBytes
--   hello world
--   
--   >>> toBytes "echo hello\\ world" & Stdio.putBytes
--   hello world
--   
--   >>> toBytes "echo 'hello world'" & Stdio.putBytes
--   hello world
--   
--   >>> toBytes "echo \"hello world\"" & Stdio.putBytes
--   hello world
--   
-- -- Pre-release toBytes :: (MonadAsync m, MonadCatch m) => String -> Stream m Word8 -- |
--   >>> toChunks "echo hello world" & Stdio.putChunks
--   hello world
--   
-- -- Pre-release toChunks :: (MonadAsync m, MonadCatch m) => String -> Stream m (Array Word8) -- | Like toChunks but use the specified configuration to run the -- process. toChunksWith :: (MonadCatch m, MonadAsync m) => (Config -> Config) -> String -> Stream m (Array Word8) -- |
--   >>> toChars "echo hello world" & Stdio.putChars
--   hello world
--   
-- -- Pre-release toChars :: (MonadAsync m, MonadCatch m) => String -> Stream m Char -- |
--   >>> toLines Fold.toList "echo -e hello\\\\nworld" & Stream.fold Fold.toList
--   ["hello","world"]
--   
-- -- Pre-release toLines :: (MonadAsync m, MonadCatch m) => Fold m Char a -> String -> Stream m a -- |
--   >>> toString "echo hello world"
--   "hello world\n"
--   
-- -- Pre-release toString :: (MonadAsync m, MonadCatch m) => String -> m String -- |
--   >>> toStdout "echo hello world"
--   hello world
--   
-- -- Pre-release toStdout :: (MonadAsync m, MonadCatch m) => String -> m () -- |
--   >>> toNull "echo hello world"
--   
-- -- Pre-release toNull :: (MonadAsync m, MonadCatch m) => String -> m () -- | pipeChunks command input runs the executable with arguments -- specified by command and supplying input stream as -- its standard input. Returns the standard output of the executable as a -- stream of byte arrays. -- -- If only the name of an executable file is specified instead of its -- path then the file name is searched in the directories specified by -- the PATH environment variable. -- -- If the input stream throws an exception or if the output stream is -- garbage collected before it could finish then the process is -- terminated with SIGTERM. -- -- If the process terminates with a non-zero exit code then a -- ProcessFailure exception is raised. -- -- The following code is equivalent to the shell command echo "hello -- world" | tr [a-z] [A-Z]: -- --
--   >>> :{
--      toChunks "echo hello world"
--    & pipeChunks "tr [a-z] [A-Z]"
--    & Stdio.putChunks
--    :}
--   HELLO WORLD
--   
-- -- Pre-release pipeChunks :: (MonadAsync m, MonadCatch m) => String -> Stream m (Array Word8) -> Stream m (Array Word8) -- | Like pipeChunks but use the specified configuration to run the -- process. pipeChunksWith :: (MonadCatch m, MonadAsync m) => (Config -> Config) -> String -> Stream m (Array Word8) -> Stream m (Array Word8) -- | Like pipeChunks except that it works on a stream of bytes -- instead of a stream of chunks. -- --
--   >>> :{
--      toBytes "echo hello world"
--    & pipeBytes "tr [a-z] [A-Z]"
--    & Stdio.putBytes
--    :}
--   HELLO WORLD
--   
-- -- Pre-release pipeBytes :: (MonadAsync m, MonadCatch m) => String -> Stream m Word8 -> Stream m Word8 -- | Like pipeChunks except that it works on a stream of chars -- instead of a stream of chunks. -- --
--   >>> :{
--      toChars "echo hello world"
--    & pipeChars "tr [a-z] [A-Z]"
--    & Stdio.putChars
--    :}
--   HELLO WORLD
--   
-- -- Pre-release pipeChars :: (MonadAsync m, MonadCatch m) => String -> Stream m Char -> Stream m Char -- | Launch a process interfacing with the user. User interrupts are sent -- to the launched process and ignored by the parent process. The -- launched process inherits stdin, stdout, and stderr from the parent, -- so that the user can interact with the process. The parent waits for -- the child process to exit, an ExitCode is returned when the -- process finishes. -- -- This is the same as the common system function found in other -- libraries used to execute commands. -- -- On Windows you can pass setSession NewConsole to create a new -- console. foreground :: (Config -> Config) -> String -> IO ExitCode -- | Launch a daemon process. Closes stdin, stdout and stderr, creates a -- new session, detached from the terminal, the parent does not wait for -- the process to finish. -- -- The ProcessHandle returned can be used to terminate the daemon -- or send signals to it. daemon :: (Config -> Config) -> String -> IO ProcessHandle -- | Launch a standlone process i.e. the process does not have a way to -- attach the IO streams with other processes. The IO streams stdin, -- stdout, stderr can either be inherited from the parent or closed. -- -- This API is more powerful than interactive and daemon -- and can be used to implement both of these. However, it should be used -- carefully e.g. if you inherit the IO streams and parent is not waiting -- for the child process to finish then both parent and child may use the -- IO streams resulting in garbled IO if both are reading/writing -- simultaneously. -- -- If the parent chooses to wait for the process an ExitCode is -- returned otherwise a ProcessHandle is returned which can be -- used to terminate the process, send signals to it or wait for it to -- finish. standalone :: Bool -> (Bool, Bool, Bool) -> (Config -> Config) -> String -> IO (Either ExitCode ProcessHandle) -- | This module provides functions to run operating system processes as -- stream producers, consumers or stream transformation functions. Thus -- OS processes can be used in the same way as Haskell functions and all -- the streaming combinators in streamly can be used to combine them. -- This allows you to seamlessly integrate external binary executables -- into your Haskell program. -- -- However, we recommend native Haskell functions with Streamly threads -- over using system processes whenever possible. This approach offers a -- simpler programming model compared to system processes, which also -- have a larger performance overhead. -- -- Prefer Streamly.System.Command module as a higher level wrapper -- over this module. -- --

Executables as functions

-- -- Processes can be composed in a streaming pipeline just like a Posix -- shell command pipeline. Moreover, we can mix processes and Haskell -- functions seamlessly in a processing pipeline. For example: -- --
--   >>> :{
--      Process.toBytes "echo" ["hello world"]
--    & Process.pipeBytes "tr" ["[a-z]", "[A-Z]"]
--    & Stream.fold Stdio.write
--    :}
--    HELLO WORLD
--   
-- -- Of course, you can use a Haskell function instead of "tr": -- --
--   >>> :{
--      Process.toBytes "echo" ["hello world"]
--    & Unicode.decodeLatin1 & fmap toUpper & Unicode.encodeLatin1
--    & Stream.fold Stdio.write
--    :}
--    HELLO WORLD
--   
-- --

Shell commands as functions

-- -- Using a shell as the command interpreter we can use shell commands in -- a data processing pipeline: -- --
--   >>> :{
--      Process.toBytes "sh" ["-c", "echo hello | tr [a-z] [A-Z]"]
--    & Stream.fold Stdio.write
--    :}
--    HELLO
--   
-- --

Running Commands Concurrently

-- -- We can run executables or commands concurrently as we would run any -- other functions in Streamly. For example, the following program greps -- the word "to" in all the files in the current directory concurrently: -- --
--   >>> :{
--   grep file =
--      Process.toBytes "grep" ["-H", "pattern", file]
--    & Stream.handle (\(_ :: Process.ProcessFailure) -> Stream.nil)
--    & Stream.foldMany (Fold.takeEndBy (== 10) Array.write)
--    :}
--   
-- --
--   >>> :{
--   pgrep =
--      Dir.readFiles "."
--    & Stream.parConcatMap id grep
--    & Stream.fold Stdio.writeChunks
--   :}
--   
-- --

Experimental APIs

-- -- See Streamly.Internal.System.Process for unreleased functions. module Streamly.System.Process -- | An exception that is raised when a process fails. newtype ProcessFailure -- | The exit code of the process. ProcessFailure :: Int -> ProcessFailure -- | Process configuration used for creating a new process. -- -- By default the process config is setup to inherit the following -- attributes from the parent process: -- -- -- -- On POSIX: -- -- -- -- On Windows by default the parent process waits for the entire child -- process tree to finish. data Config -- | Set the current working directory of the new process. When -- Nothing, the working directory is inherited from the parent -- process. -- -- Default is Nothing - inherited from the parent process. setCwd :: Maybe FilePath -> Config -> Config -- | Set the environment variables for the new process. When -- Nothing, the environment is inherited from the parent process. -- -- Default is Nothing - inherited from the parent process. setEnv :: Maybe [(String, String)] -> Config -> Config -- | Close all open file descriptors inherited from the parent process. -- Note, this does not apply to stdio descriptors - the behavior of those -- is determined by other configuration settings. -- -- Default is False. -- -- Note: if the number of open descriptors is large, it may take a while -- closing them. closeFiles :: Bool -> Config -> Config -- | If True the new process starts a new process group, becomes a -- process group leader, its pid becoming the process group id. -- -- See the POSIX setpgid man page. -- -- Default is False, the new process belongs to the parent's -- process group. newProcessGroup :: Bool -> Config -> Config -- | InheritSession makes the new process inherit the terminal -- session from the parent process. This is the default. -- -- NewSession makes the new process start with a new session -- without a controlling terminal. On POSIX, setsid is used to -- create a new process group and session, the pid of the new process is -- the session id and process group id as well. On Windows -- DETACHED_PROCESS flag is used to detach the process from -- inherited console session. -- -- NewConsole creates a new terminal and attaches the process to -- the new terminal on Windows, using the CREATE_NEW_CONSOLE flag. On -- POSIX this does nothing. -- -- For Windows see -- -- -- -- For POSIX see, setsid man page. data Session -- | Inherit the parent session InheritSession :: Session -- | Detach process from the current session NewSession :: Session -- | Windows only, CREATE_NEW_CONSOLE flag NewConsole :: Session -- | Define the terminal session behavior for the new process. -- -- Default is InheritSession. setSession :: Session -> Config -> Config -- | When this is True, the parent process ignores user interrupt -- signals SIGINT and SIGQUIT delivered to it until the -- child process exits. If multiple child processes are started then the -- default handling in the parent is restored only after the last one -- exits. -- -- When a user presses CTRL-C or CTRL- on the terminal, a SIGINT or -- SIGQUIT is sent to all the foreground processes in the terminal -- session, this includes both the child and the parent. By default, on -- receiving these signals, the parent process would cleanup and exit, to -- avoid that and let the child handle these signals we can choose to -- ignore these signals in the parent until the child exits. -- -- POSIX only. Default is False. interruptChildOnly :: Bool -> Config -> Config -- | Use the POSIX setuid call to set the user id of the new process -- before executing the command. The parent process must have sufficient -- privileges to set the user id. -- -- POSIX only. See the POSIX setuid man page. -- -- Default is Nothing - inherit from the parent. setUserId :: Maybe Word32 -> Config -> Config -- | Use the POSIX setgid call to set the group id of the new -- process before executing the command. The parent process must have -- sufficient privileges to set the group id. -- -- POSIX only. See the POSIX setgid man page. -- -- Default is Nothing - inherit from the parent. setGroupId :: Maybe Word32 -> Config -> Config -- | On Windows, the parent waits for the entire descendant tree of process -- i.e. including processes that are spawned by the child process. -- -- Default is True. waitForDescendants :: Bool -> Config -> Config -- | The following code is equivalent to the shell command echo "hello -- world": -- --
--   >>> :{
--      Process.toChunks "echo" ["hello world"]
--    & Stream.fold Stdio.writeChunks
--    :}
--   hello world
--   
-- --
--   >>> toChunks = toChunksWith id
--   
toChunks :: (MonadAsync m, MonadCatch m) => FilePath -> [String] -> Stream m (Array Word8) -- | Like toChunks but use the specified configuration to run the -- process. toChunksWith :: (MonadCatch m, MonadAsync m) => (Config -> Config) -> FilePath -> [String] -> Stream m (Array Word8) -- | The following code is equivalent to the shell command echo "hello -- world": -- --
--   >>> :{
--      Process.toBytes "echo" ["hello world"]
--    & Stream.fold Stdio.write
--    :}
--   hello world
--   
toBytes :: (MonadAsync m, MonadCatch m) => FilePath -> [String] -> Stream m Word8 -- |
--   >>> toChars path args = toBytes path args & Unicode.decodeUtf8
--   
toChars :: (MonadAsync m, MonadCatch m) => FilePath -> [String] -> Stream m Char -- |
--   >>> toLines path args f = toChars path args & Unicode.lines f
--   
toLines :: (MonadAsync m, MonadCatch m) => Fold m Char a -> FilePath -> [String] -> Stream m a -- |
--   >>> toString path args = toChars path args & Stream.fold Fold.toList
--   
toString :: (MonadAsync m, MonadCatch m) => FilePath -> [String] -> m String -- |
--   >>> toStdout path args = toChunks path args & Stdio.putChunks
--   
toStdout :: (MonadAsync m, MonadCatch m) => FilePath -> [String] -> m () -- |
--   >>> toNull path args = toChunks path args & Stream.fold Fold.drain
--   
toNull :: (MonadAsync m, MonadCatch m) => FilePath -> [String] -> m () -- | pipeChunks file args input runs the executable file -- specified by its name or path using args as arguments and -- input stream as its standard input. Returns the standard -- output of the executable as a stream. -- -- If only the name of an executable file is specified instead of its -- path then the file name is searched in the directories specified by -- the PATH environment variable. -- -- If the input stream throws an exception or if the output stream is -- garbage collected before it could finish then the process is -- terminated with SIGTERM. -- -- If the process terminates with a non-zero exit code then a -- ProcessFailure exception is raised. -- -- The following code is equivalent to the shell command echo "hello -- world" | tr [a-z] [A-Z]: -- --
--   >>> :{
--      Process.toChunks "echo" ["hello world"]
--    & Process.pipeChunks "tr" ["[a-z]", "[A-Z]"]
--    & Stream.fold Stdio.writeChunks
--    :}
--   HELLO WORLD
--   
-- -- pre-release pipeChunks :: (MonadCatch m, MonadAsync m) => FilePath -> [String] -> Stream m (Array Word8) -> Stream m (Array Word8) -- | Like pipeChunks but use the specified configuration to run the -- process. pipeChunksWith :: (MonadCatch m, MonadAsync m) => (Config -> Config) -> FilePath -> [String] -> Stream m (Array Word8) -> Stream m (Array Word8) -- | Like pipeChunks except that it works on a stream of bytes -- instead of a stream of chunks. -- -- We can write the example in pipeChunks as follows. -- --
--   >>> :{
--      Process.toBytes "echo" ["hello world"]
--    & Process.pipeBytes "tr" ["[a-z]", "[A-Z]"]
--    & Stream.fold Stdio.write
--    :}
--   HELLO WORLD
--   
-- -- pre-release pipeBytes :: (MonadCatch m, MonadAsync m) => FilePath -> [String] -> Stream m Word8 -> Stream m Word8 -- | toBytesEither path args runs the executable at path -- using args as arguments and returns a stream of Either -- bytes. The Left values are from stderr and the -- Right values are from stdout of the executable. -- -- Raises ProcessFailure exception in case of failure. -- -- The following example uses echo to write hello to -- stdout and world to stderr, then uses folds -- from Streamly.Console.Stdio to write them back to -- stdout and stderr respectively: -- --
--   >>> :{
--     Process.toBytesEither "/bin/bash" ["-c", "echo 'hello'; echo 'world' 1>&2"]
--   & Stream.fold (Fold.partition Stdio.writeErr Stdio.write)
--   :}
--   world
--   hello
--   ((),())
--   
toBytesEither :: (MonadAsync m, MonadCatch m) => FilePath -> [String] -> Stream m (Either Word8 Word8) -- | Like toBytesEither but generates a stream of Array -- Word8 instead of a stream of Word8. -- --
--   >>> :{
--     toChunksEither "bash" ["-c", "echo 'hello'; echo 'world' 1>&2"]
--   & Stream.fold (Fold.partition Stdio.writeErrChunks Stdio.writeChunks)
--   :}
--   world
--   hello
--   ((),())
--   
-- --
--   >>> toChunksEither = toChunksEitherWith id
--   
-- -- Prefer toChunksEither over toBytesEither when -- performance matters. -- -- Pre-release toChunksEither :: (MonadAsync m, MonadCatch m) => FilePath -> [String] -> Stream m (Either (Array Word8) (Array Word8)) -- | Like toChunksEither but use the specified configuration to run -- the process. toChunksEitherWith :: (MonadCatch m, MonadAsync m) => (Config -> Config) -> FilePath -> [String] -> Stream m (Either (Array Word8) (Array Word8)) -- | pipeBytesEither path args input runs the executable at -- path using args as arguments and input -- stream as its standard input. The error stream of the executable is -- presented as Left values in the resulting stream and output -- stream as Right values. -- -- Raises ProcessFailure exception in case of failure. -- -- For example, the following is equivalent to the shell command echo -- "hello world" | tr [:lower:] [:upper:]: -- --
--   >>> :{
--      pipeBytesEither "echo" ["hello world"] Stream.nil
--    & Stream.catRights
--    & pipeBytesEither "tr" ["[:lower:]", "[:upper:]"]
--    & Stream.catRights
--    & Stream.fold Stdio.write
--    :}
--   HELLO WORLD
--   
pipeBytesEither :: (MonadCatch m, MonadAsync m) => FilePath -> [String] -> Stream m Word8 -> Stream m (Either Word8 Word8) -- | Like pipeChunks but also includes stderr as Left stream -- in the Either output. pipeChunksEither :: (MonadCatch m, MonadAsync m) => FilePath -> [String] -> Stream m (Array Word8) -> Stream m (Either (Array Word8) (Array Word8)) -- | Like pipeChunksEither but use the specified configuration to -- run the process. pipeChunksEitherWith :: (MonadCatch m, MonadAsync m) => (Config -> Config) -> FilePath -> [String] -> Stream m (Array Word8) -> Stream m (Either (Array Word8) (Array Word8)) -- | Launch a process interfacing with the user. User interrupts are sent -- to the launched process and ignored by the parent process. The -- launched process inherits stdin, stdout, and stderr from the parent, -- so that the user can interact with the process. The parent waits for -- the child process to exit, an ExitCode is returned when the -- process finishes. -- -- This is the same as the common system function found in other -- libraries used to execute commands. -- -- On Windows you can pass setSession NewConsole to create a new -- console. foreground :: (Config -> Config) -> FilePath -> [String] -> IO ExitCode -- | Launch a daemon process. Closes stdin, stdout and stderr, creates a -- new session, detached from the terminal, the parent does not wait for -- the process to finish. -- -- The ProcessHandle returned can be used to terminate the daemon -- or send signals to it. daemon :: (Config -> Config) -> FilePath -> [String] -> IO ProcessHandle -- | Launch a standalone process i.e. the process does not have a way to -- attach the IO streams with other processes. The IO streams stdin, -- stdout, stderr can either be inherited from the parent or closed. -- -- This API is more powerful than interactive and daemon -- and can be used to implement both of these. However, it should be used -- carefully e.g. if you inherit the IO streams and parent is not waiting -- for the child process to finish then both parent and child may use the -- IO streams resulting in garbled IO if both are reading/writing -- simultaneously. -- -- If the parent chooses to wait for the process an ExitCode is -- returned otherwise a ProcessHandle is returned which can be -- used to terminate the process, send signals to it or wait for it to -- finish. standalone :: Bool -> (Bool, Bool, Bool) -> (Config -> Config) -> FilePath -> [String] -> IO (Either ExitCode ProcessHandle) -- | Deprecated: Please use pipeChunks instead. processChunks :: (MonadCatch m, MonadAsync m) => FilePath -> [String] -> Stream m (Array Word8) -> Stream m (Array Word8) -- | Deprecated: Please use pipeBytes instead. processBytes :: (MonadCatch m, MonadAsync m) => FilePath -> [String] -> Stream m Word8 -> Stream m Word8