(index ("run" 0) ("&" 0) ("exec-epf" 0) ("run/port" 5642) ("run/file" 5642) ("run/string" 5642) ("run/strings" 5642) ("run/sexp" 5642) ("run/sexps" 5642) ("&&" 7197) ("" 7197) ("run/collecting" 7894) ("fork" 8637) ("%fork" 8637) ("wait" 9448) ("signal-process" 10877) ("process-sleep" 11135) ("process?" 11323) ("proc?" 11323) ("proc:pid" 11578) ("fork/pipe" 11718) ("%fork/pipe" 11718) ("fork/pipe+" 12964) ("fork%/pipe+" 12964) ("exec-path" 13541) ("exec-path*" 13541) ("run/port*" 14366) ("run/file*" 14366) ("run/string*" 14366) ("run/strings*" 14366) ("run/sexp*" 14366) ("run/sexps*" 14366) ("run/collecting*" 14949))
(def (sig (syntax "(run pf [redirection ...])" (id run)) (syntax "(& pf [redirection ...])" (id &)) (syntax "(exec-epf pf [redirection ...])" (id exec-epf))) (p "These forms run a new process pipeline, described by process form " (tt "pf") ", with optional redirections of input and output descriptors indicated by any number of " (tt "redirection") " patterns.  These processes don't interact with the calling process; if you want to capture their output there are other forms, like " (tt "run/string") ", " (tt "run/port") " and friends.  Please also beware that these forms don't do anything to indicate nonzero exit statuses in pipelines.  The " (tt "&&") " and " (tt "||") " macros might help if you need to do status checks.") (p "The " (tt "run") " form simply runs the process and waits for the final process in the pipeline to exit.  It returns three values relating to this final process:") (ul (li "Either its exit status as an integer if the process terminated normally, or the signal number that terminated/stopped the process.") (li "#t if the process exited normally, #f if it terminated abnormally.") (li "Its process id (an integer).")) (p "You'll note that these are just the same values returned by " (tt "process-wait") ", but in a slightly different order (" (tt "wait") " utilises this modified order, as well).  This is for compatibility reasons: In SCSH this form returns only the exit status, but because Chicken accepts multiple values in single value contexts (discarding all but the first), we can provide the other ones as extra values, thereby avoiding gratuitous incompatibility.") (p "The " (tt "&") " form simply runs the process in the background and returns one value: a process object representing the last process in the pipeline.") (p "The " (tt "exec-epf") " form never returns; it replaces the current process by the last process in the pipeline.  The others are implemented in terms of this form:") (highlight scheme "(& . epf) => (process-fork (lambda () (exec-epf . epf)) #t)\n(run . epf) => (process-wait (& . epf))") (p "A process form followed by a set of redirections is called \"extended process form\" in SCSH terminology.  A process form can be one of the following:") (highlight scheme "(<program> <arg> ...)\n(pipe <pf> ...)\n(pipe+ <connect-list> <pf> ...)\n(epf <pf> <redirection> ...)\n(begin <s-expr> ...)") (p "The arguments to a " (tt "<program>") " rule are implicitly quasiquoted.  The basic building blocks are " (tt "<program>") " and " (tt "<begin>") ", which always correspond to one process in a pipeline.  The other rules are ways to combine these two.") (p "The " (tt "pipe") " rule will hook up standard output and standard error of each " (tt "pf") " to the next " (tt "pf") "'s standard input, just like in a regular Unix shell pipeline.") (p "The " (tt "pipe+") " rule is like pipe, but it allows you to hook up arbitrary file descriptors between two neighbouring processes.  This is done through the " (tt "connect-list") ", which is a list of fd-mappings describing how ports are connected from one process to the next.  It has the form " (tt "((from-fd1 from-fd2 ... to-fd) ...)") ".  The " (tt "from-fd") "s correspond to outbound file descriptors in one process, the " (tt "to-fd") "s correspond to inbound file descriptors in the other process.") (p "The " (tt "epf") " rule is to get extended process forms in contexts where only process forms are accepted, like the " (tt "pipe") " and " (tt "pipe+") " subforms, and in the " (tt "&&") " and " (tt "||") " macros (so you can do file redirects here).") (p "The " (tt "begin") " rule allows you to write scheme code which will be run in a forked process, having its " (tt "current-input-port") ", " (tt "current-output-port") " and " (tt "current-error-port") " hooked up to its neighbouring processes in the pipeline.") (p "A redirection can be one of the following:") (highlight scheme "(> [<fd>] <file-name>)      ; Write fd (default: 1) to the given filename\n(>> [<fd>] <file-name>)     ; Like >, but append instead of overwriting\n(< [<fd>] <file-name>)      ; Read fd (default: 0) from the filename\n(<< [<fd>] <scheme-object>) ; Like <, but use object's printed representation\n(= <fd> <fd-or-port>)       ; Redirect fd to fd-or-port\n(- <fd-or-port>)            ; Close fd\nstdports                    ; Duplicate fd 0, 1, 2 from standard Scheme ports") (p "The arguments to redirection rules are also implicitly quasiquoted.") (p "To tie it all together, here are a few examples:") (highlight scheme "(use scsh-process)\n\n;; Writes \"1235\" to a file called \"out\" in the current directory.\n;; Shell equivalent:  echo 1234 + 1 | bc > out\n(run (pipe (echo \"1234\" + 1) (\"bc\")) (> out))\n\n(define message \"hello, world\")\n\n;; Writes 13 to stdout, with a forked Scheme process writing the data.\n;; Shell equivalent (sort of): echo 'hello, world' | wc -c\n(run (pipe (begin (display message) (newline)) (wc -c)))\n\n;; A verbose way of doing the same, using pipe+.  It connects the {{begin}}\n;; form's standard output and standard error to standard input of {{wc}}:\n(run (pipe+ ((1 2 0)) (begin (display message) (newline)) (wc -c)))\n\n;; Same as above, using redirection instead of port writing:\n(run (wc -c) (<< ,(string-append message \"\\n\")))\n\n;; Writes nothing because stdout is closed:\n(run (wc -c) (<< ,message) (- 1))\n\n;; A complex toy example using nested pipes, with input/output redirection.\n;; Closest shell equivalent:\n;; ((sh -c \"echo foo >&2\") 2>&1 | cat) | cat\n(run (pipe+ ((1 0))\n            (pipe+ ((2 0)) (sh -c \"echo foo >&2\") (cat))\n            (cat)))"))
(def (sig (syntax "(run/port pf [redirection ...])" (id run/port)) (syntax "(run/file pf [redirection ...])" (id run/file)) (syntax "(run/string pf [redirection ...])" (id run/string)) (syntax "(run/strings pf [redirection ...])" (id run/strings)) (syntax "(run/sexp pf [redirection ...])" (id run/sexp)) (syntax "(run/sexps pf [redirection ...])" (id run/sexps))) (p "These forms are equivalent to " (tt "run") ", except they wire up the current process to the endpoint of the pipeline, allowing you to read the " (i "standard output") " from the pipeline as a whole.  If you also need standard error or have even more specialized needs, take a look at the " (tt "run/collecting") " form.") (p "The difference between these forms is in how this output is returned, and when the call returns:") (ul (li (tt "run/port") " immediately returns after forking, and returns a port from which you can read.") (li (tt "run/file") " returns after the final process exits, resulting in a string which indicates a temporary file containing the process' output.") (li (tt "run/string") " returns when the process closes its standard output (ie, when EOF is read), collecting the standard output into a string.") (li (tt "run/strings") " is like " (tt "run/string") ", but returns a list of strings, split at newline characters.") (li (tt "run/sexp") " reads an s-expression, and returns as soon as a complete s-expression was read.") (li (tt "run/sexps") " reads all s-expressions until eof, returning a list of s-expressions.  It returns as soon as EOF is read.")))
(def (sig (syntax "(&& pf ...)" (id &&)) (syntax "(|| pf ...)" (id ||))) (p "These macros act like their counterpart shell operators; they run the given process forms in sequence and stop either on the first \"false\" value (nonzero exit) or on the first \"true\" value (zero exit), respectively.") (p "The result value of these is " (tt "#f") " or " (tt "#t") ", so they act a lot like regular Scheme " (tt "and") " and " (tt "or") ".") (p (b "Note:") " The name of the " (tt "||") " macro is really the empty symbol whereas in SCSH's reader, it reads as a symbol consisting of two pipe characters.  The name of these macros may change in the future if it turns out to cause too much trouble."))
(def (sig (syntax "(run/collecting fds pf ...)" (id run/collecting))) (p "This form runs the " (tt "pf") " form, redirecting each the file descriptors in the " (tt "fds") " list to a separate tempfile, and waits for the process to complete.") (p "The result of this expression is " (tt "(status file ...)") ".  " (tt "status") " is the exit status of the process.  Each " (tt "file") " entry is an opened input port for the temporary file that belongs to the file descriptor at the same offset in the " (tt "fds") " list.  If you close the port, the tempfile is removed.") (p "See the " (link "http://www.scsh.net/docu/html/man-Z-H-3.html#node_sec_2.4.2" "SCSH documentation") " for an extended rationale of why this works the way it does."))
(def (sig (procedure "(fork [thunk [continue-threads?]])" (id fork)) (procedure "(%fork [thunk [continue-threads?]])" (id %fork))) (p "If " (tt "thunk") " is provided and not " (tt "#f") ", the child process will invoke the thunk and exit when it returns.  If " (tt "continue-threads") " is provided and " (tt "#t") ", all existing threads will be kept alive in the child process; by default only the current thread will be kept alive.") (p (tt "fork") " differs from the regular " (tt "process-fork") " in its return value. Instead of a pid value, this returns a process object representing the child is returned in the parent process.  When " (tt "thunk") " is not provided, " (tt "#f") " (not zero!) is returned in the child process.") (p "Currently " (tt "%fork") " is just an alias for " (tt "fork") "."))
(def (sig (procedure "(wait [pid-or-process [nohang]])" (id wait))) (p "Like " (tt "process-wait") ", but nonblocking: Suspends the current process until the child process described by " (tt "pid-or-process") " (either a numerical process ID or a scsh-process object) has terminated using the UNIX system call waitpid(). If " (tt "pid-or-process") " is not given, then this procedure waits for any child process. If " (tt "nohang") " is given and not " (tt "#f") " then the current process is not suspended.") (p "This procedure returns three values, " (b "in a different order from " (tt "process-wait")) ":") (ul (li "Either the exit status, if the process terminated normally or the signal number that terminated/stopped the process.") (li "#t if the process exited normally or #f otherwise.") (li (tt "pid") " or 0")) (p "All values are " (tt "#f") ", if " (tt "nohang") " is true and the child process has not terminated yet.") (p "It is allowed to wait multiple times for the same process after it has completed, if you pass a process object.  Process IDs can only be waited for once after they have completed and will cause an error otherwise.") (p "Since version 0.8 of scsh-process, this procedure is nonblocking, which means you can " (tt "wait") " for a child in a thread and have the other threads continue to run; the process isn't suspended, but a signal handler will take care of unblocking the waiting thread."))
(def (sig (procedure "(signal-process proc signal)" (id signal-process))) (p "Like " (tt "process-signal") " from the POSIX unit, but accepts a process object (" (tt "proc") ") instead of a pid.  Sends " (tt "signal") " (an integer) to the given process."))
(def (sig (procedure "(process-sleep sec)" (id process-sleep))) (p "Put the entire process to sleep for " (tt "sec") " seconds.  Just an alias for " (tt "sleep") " from the POSIX unit."))
(def (sig (procedure "(process? object)" (id process?)) (procedure "(proc? object)" (id proc?))) (p "Is " (tt "object") " an object representing a process?  The " (tt "process?") " predicate is deprecated; " (tt "proc?") " is API-compatible with SCSH."))
(def (sig (procedure "(proc:pid proc)" (id proc:pid))) (p "Retrieve the process id (an integer) from the process object " (tt "proc") "."))
(def (sig (procedure "(fork/pipe [thunk [continue-threads?]])" (id fork/pipe)) (procedure "(%fork/pipe [thunk [continue-threads?]])" (id %fork/pipe))) (p "These fork the process as per " (tt "fork") " or " (tt "%fork") ", but additionally they set up a pipe between parent and child.  The child's standard output is set up to write to the pipe, while the parent's standard input is set up to read to the pipe.  Standard error is inherited from the parent.") (p "The return value is a process object or " (tt "#f") ".") (p "Currently " (tt "fork%/pipe") " is just an alias for " (tt "fork/pipe") ".") (p (b "Important") ": These procedures only set up the file descriptors, not the Scheme ports.  " (tt "current-input-port") ", " (tt "current-output-port") " and " (tt "current-error-port") " still refer to their old file descriptors after a fork.  This means that you'll need to reopen the descriptors to get a Scheme port that reads from the child or writes to the parent:") (highlight scheme "(use scsh-process posix)\n\n(process-wait\n  (fork/pipe (lambda ()\n               (with-output-to-port (open-output-file* 1)\n                 (lambda () (display \"Hello, world.\\n\"))))))\n\n(read-line (open-input-file* 0)) => \"Hello, world\""))
(def (sig (procedure "(fork/pipe+ conns [thunk [continue-threads?]])" (id fork/pipe+)) (procedure "(fork%/pipe+ conns [thunk [continue-threads?]])" (id fork%/pipe+))) (p "These are like " (tt "fork/pipe") " and " (tt "fork%/pipe") ", except they allow you to control how the file descriptors are wired.  Conns is a list of lists, of the form " (tt "((from-fd1 from-fd2 ... to-fd) ...)") ".  See the description of " (tt "pipe+") " under the " (tt "run") " special form for more information.") (p "Currently " (tt "fork%/pipe+") " is just an alias for " (tt "fork/pipe+") "."))
(def (sig (procedure "(exec-path program [args ...])" (id exec-path)) (procedure "(exec-path* program arg-list)" (id exec-path*))) (p "This will simply execute " (tt "program") " with the given arguments in " (tt "args") " or " (tt "args-list") ".  The program replaces the currently running process.  All arguments (including " (tt "program") ") must be strings, symbols or numbers, and they will automatically be converted to strings before invoking the program.") (p "The program is looked up in the user's path, which is simply the " (tt "$PATH") " environment variable.  There currently is no separately maintained path list like in SCSH.  The difference between the two procedures is that " (tt "exec-path") " accepts a variable number of arguments and " (tt "exec-path*") " requires them pre-collected into a list."))
(def (sig (procedure "(run/port* thunk)" (id run/port*)) (procedure "(run/file* thunk)" (id run/file*)) (procedure "(run/string* thunk)" (id run/string*)) (procedure "(run/strings* thunk)" (id run/strings*)) (procedure "(run/sexp* thunk)" (id run/sexp*)) (procedure "(run/sexps* thunk)" (id run/sexps*))) (p "These set up a pipe between the current process and a forked off child process which runs the " (tt "thunk") ". See the \"unstarred\" versions " (tt "run/port") ", " (tt "run/file") " ... " (tt "run/sexps") " for more information about the semantics of these procedures."))
(def (sig (procedure "(run/collecting* fds thunk)" (id run/collecting*))) (p "Like " (tt "run/collecting") ", but use a " (tt "thunk") " instead of a process form."))
