diff --git a/DESCRIPTION b/DESCRIPTION
index 2cf116c4..51d924be 100644
--- a/DESCRIPTION
+++ b/DESCRIPTION
@@ -42,4 +42,4 @@ Config/testthat/edition: 3
Config/usethis/last-upkeep: 2025-04-25
Encoding: UTF-8
Roxygen: list(markdown = TRUE)
-Config/roxygen2/version: 7.3.3.9000
+RoxygenNote: 7.3.3
diff --git a/NAMESPACE b/NAMESPACE
index 40042131..898cafa3 100644
--- a/NAMESPACE
+++ b/NAMESPACE
@@ -23,6 +23,7 @@ export(conn_create_fd)
export(conn_create_fifo)
export(conn_create_file)
export(conn_create_pipepair)
+export(conn_create_proc_pipepair)
export(conn_create_unix_socket)
export(conn_disable_inheritance)
export(conn_file_name)
@@ -38,6 +39,7 @@ export(conn_write)
export(curl_fds)
export(default_pty_options)
export(is_valid_fd)
+export(pipeline)
export(poll)
export(process)
export(processx_conn_close)
diff --git a/NEWS.md b/NEWS.md
index 25070816..2f08ec81 100644
--- a/NEWS.md
+++ b/NEWS.md
@@ -1,5 +1,10 @@
# processx (development version)
+* New experimental `pipeline` R6 class for running two or more processes
+ connected by kernel-level pipes, like a Unix shell pipeline
+ (`cmd1 | cmd2 | cmd3`). Data flows directly between child processes
+ without passing through R. Works on Unix and Windows (#280).
+
* New "Process cleanup" article.
* New `linux_pdeathsig` argument to `process$new()`: on Linux, the child
diff --git a/R/connections.R b/R/connections.R
index c0e84562..18df7636 100644
--- a/R/connections.R
+++ b/R/connections.R
@@ -274,6 +274,22 @@ conn_create_pipepair <- function(encoding = "", nonblocking = c(TRUE, FALSE)) {
chain_call(c_processx_connection_create_pipepair, encoding, nonblocking)
}
+#' @details
+#' `conn_create_proc_pipepair()` creates a unidirectional pipe suitable for
+#' connecting two child processes: the first element is the write end (pass as
+#' `stdout` to the writing process) and the second is the read end (pass as
+#' `stdin` to the reading process). Unlike `conn_create_pipepair()`, both ends
+#' are synchronous (blocking), which is required for child-process stdin/stdout
+#' on Windows.
+#'
+#' @rdname processx_connections
+#' @export
+
+conn_create_proc_pipepair <- function(encoding = "") {
+ assert_that(is_string(encoding))
+ chain_call(c_processx_connection_create_proc_pipepair, encoding)
+}
+
#' @details
#' `conn_read_chars()` reads UTF-8 characters from the connections. If the
#' connection itself is not UTF-8 encoded, it re-encodes it.
@@ -427,9 +443,9 @@ processx_conn_write <- function(con, str, sep = "\n", encoding = "") {
#' @details
#' `conn_create_file()` creates a connection to a file.
#'
-#' @param filename File name. For `conn_create_pipe()` on Windows, a
+#' @param filename File name. For `conn_create_fifo()` on Windows, a
#' `\\?\pipe` prefix is added to this, if it does not have such a prefix.
-#' For `conn_create_pipe()` it can also be `NULL`, in which case a random
+#' For `conn_create_fifo()` it can also be `NULL`, in which case a random
#' file name is used via `tempfile()`.
#' @param read Whether the connection is readable.
#' @param write Whethe the connection is writeable.
diff --git a/R/pipeline.R b/R/pipeline.R
new file mode 100644
index 00000000..bf2db85f
--- /dev/null
+++ b/R/pipeline.R
@@ -0,0 +1,314 @@
+#' Pipeline of processes connected with pipes
+#'
+#' @description
+#' `r lifecycle::badge("experimental")`
+#'
+#' A `pipeline` object represents a sequence of processes whose standard
+#' input and output streams are connected with pipes, like a Unix pipeline
+#' (`cmd1 | cmd2 | cmd3`). Data flows directly between the child processes
+#' via kernel-level pipes — the parent R process sees only the output of the
+#' final command (when `stdout = "|"`).
+#'
+#' @param cmds A non-empty list of character vectors. Each vector is one
+#' command: the first element is the executable and the rest are its
+#' arguments. Example: `list(c("sort"), c("uniq", "-c"))`.
+#' @param stdin Standard input for the *first* process. `NULL` to discard,
+#' `"|"` so the parent R process can write to it via `$write_input()`, or
+#' a file path.
+#' @param stdout Standard output of the *last* process. `"|"` (the default)
+#' so the parent R process can read from it, `NULL` to discard, or a file
+#' path.
+#' @param stderr Standard error for *all* processes. `NULL` (the default) to
+#' discard, `"|"` to create a separate readable pipe per process, `"2>&1"`
+#' to merge into stdout, or a file path. When `"|"`, use
+#' `$read_error()` to read from the last process; use `$get_processes()`
+#' to access individual process objects for other processes.
+#' @param env Environment variables for all processes, or `NULL` to inherit
+#' the parent environment.
+#' @param encoding Assumed encoding for stdin/stdout/stderr streams.
+#' @param wd Working directory for all processes, or `NULL` for the current
+#' directory.
+#' @param cleanup Whether to kill the processes on garbage collection.
+#' @param cleanup_tree Whether to kill the full process trees on garbage
+#' collection.
+#' @param n Number of characters or lines to read. -1 means all available.
+#' @param str String to write to the process stdin.
+#' @param sep Separator to add after `str`.
+#' @param timeout Timeout in milliseconds. -1 means no timeout.
+#' @param grace Grace period in seconds before sending SIGKILL (Unix) or
+#' terminating forcefully (Windows). Currently not used.
+#' @param close_connections Whether to close connections after killing.
+#' @param ... Not used, for compatibility with the generic.
+#'
+#' @section Methods:
+#' `pipeline$new(cmds, stdin, stdout, stderr, env, encoding, wd,
+#' cleanup, cleanup_tree)`
+#'
+#' `$read_output(n = -1)`, `$read_output_lines(n = -1)`,
+#' `$read_all_output()`, `$read_all_output_lines()` — read from the last
+#' process (only meaningful when `stdout = "|"`).
+#'
+#' `$poll_io(timeout)` — poll the last process's connections for I/O.
+#'
+#' `$read_error(n = -1)`, `$read_error_lines(n = -1)`,
+#' `$read_all_error()`, `$read_all_error_lines()` — read stderr of the
+#' last process (only meaningful when `stderr = "|"`).
+#'
+#' `$write_input(str, sep = "\n")` — write to first process stdin
+#' (only meaningful when `stdin = "|"`).
+#'
+#' `$close_input()` — close the first process stdin, signalling EOF.
+#'
+#' `$wait(timeout = -1)` — wait for all processes to finish.
+#'
+#' `$kill(grace = 0.1, close_connections = TRUE)` — kill all processes.
+#'
+#' `$kill_tree(grace = 0.1, close_connections = TRUE)` — kill all
+#' process trees.
+#'
+#' `$is_alive()` — returns `TRUE` if any process is still running.
+#'
+#' `$get_exit_statuses()` — list of exit codes (one per process; `NULL`
+#' if still running).
+#'
+#' `$get_pids()` — integer vector of process IDs.
+#'
+#' `$get_processes()` — list of [process] objects, one per command.
+#'
+#' `$format()` — string representation of the pipeline.
+#'
+#' `$print()` — print the pipeline to the screen.
+#'
+#' @examples
+#' \dontrun{
+#' # sort | uniq, reading from / writing to R
+#' pl <- pipeline$new(
+#' list(c("sort"), c("uniq")),
+#' stdin = "|", stdout = "|"
+#' )
+#' pl$write_input("b\na\nb\na\n")
+#' pl$close_input()
+#' pl$read_all_output_lines()
+#' pl$wait()
+#' pl$get_exit_statuses()
+#' }
+#'
+#' @export
+pipeline <- R6::R6Class(
+ "pipeline",
+ cloneable = FALSE,
+
+ public = list(
+
+ #' @description Create a new pipeline.
+ initialize = function(
+ cmds,
+ stdin = NULL,
+ stdout = "|",
+ stderr = NULL,
+ env = NULL,
+ encoding = "utf-8",
+ wd = NULL,
+ cleanup = TRUE,
+ cleanup_tree = FALSE
+ ) {
+ if (!is.list(cmds) || length(cmds) == 0L) {
+ throw(new_error("`cmds` must be a non-empty list of character vectors"))
+ }
+ for (i in seq_along(cmds)) {
+ if (!is.character(cmds[[i]]) || length(cmds[[i]]) == 0L) {
+ throw(new_error(paste0(
+ "`cmds[[", i, "]]` must be a non-empty character vector"
+ )))
+ }
+ }
+
+ n <- length(cmds)
+
+ ## Spawn all processes, creating one inter-process pipe per iteration
+ ## so that later children cannot inherit handles from pipes that do not
+ ## concern them. On Windows, CreateProcess with bInheritHandles=TRUE
+ ## passes every inheritable handle to the child. Creating each pipe
+ ## just before the two processes that need it are spawned — and closing
+ ## both ends in the parent immediately after — ensures no child ever
+ ## holds a stray write-end that would prevent EOF from propagating.
+ ## On Unix, O_CLOEXEC already prevents inheritance, but the same
+ ## iterative pattern keeps the logic consistent.
+ procs <- vector("list", n)
+ prev_read <- NULL ## read end of the previous inter-process pipe
+
+ for (i in seq_len(n)) {
+ cmd <- cmds[[i]]
+
+ ## Create the pipe connecting process i's stdout to process i+1's
+ ## stdin, unless this is the last process.
+ if (i < n) {
+ next_pipe <- conn_create_proc_pipepair()
+ proc_stdout <- next_pipe[[1L]] ## write end → child's stdout
+ } else {
+ next_pipe <- NULL
+ proc_stdout <- stdout
+ }
+
+ proc_stdin <- if (i == 1L) stdin else prev_read
+ ## Disable poll_connection for intermediate processes: their stdout is
+ ## a connection (not "|"), so the default formula would create an
+ ## unnecessary extra pipe.
+ proc_poll <- if (i < n) FALSE else NULL
+
+ procs[[i]] <- process$new(
+ cmd[[1L]],
+ cmd[-1L],
+ stdin = proc_stdin,
+ stdout = proc_stdout,
+ stderr = stderr,
+ env = env,
+ encoding = encoding,
+ wd = wd,
+ cleanup = cleanup,
+ cleanup_tree = cleanup_tree,
+ poll_connection = proc_poll
+ )
+
+ ## Close parent's copies immediately: the child now owns these
+ ## handles, and closing here prevents the next child from inheriting
+ ## the write-end of a pipe it should only read from.
+ if (!is.null(next_pipe)) close(next_pipe[[1L]]) ## write end → stdout of process i
+ if (!is.null(prev_read)) close(prev_read) ## read end → stdin of process i
+
+ prev_read <- if (!is.null(next_pipe)) next_pipe[[2L]] else NULL
+ }
+
+ private$procs <- procs
+ invisible(self)
+ },
+
+ ## ------------------------------------------------------------------ ##
+ ## Output (last process) ##
+ ## ------------------------------------------------------------------ ##
+
+ #' @description Read output of the last process.
+ read_output = function(n = -1) {
+ private$last()$read_output(n)
+ },
+
+ #' @description Read output lines of the last process.
+ read_output_lines = function(n = -1) {
+ private$last()$read_output_lines(n)
+ },
+
+ #' @description Read all output of the last process.
+ read_all_output = function() {
+ private$last()$read_all_output()
+ },
+
+ #' @description Read all output lines of the last process.
+ read_all_output_lines = function() {
+ private$last()$read_all_output_lines()
+ },
+
+ #' @description Poll the connections of the last process for I/O.
+ poll_io = function(timeout) {
+ private$last()$poll_io(timeout)
+ },
+
+ ## ------------------------------------------------------------------ ##
+ ## Error (last process) ##
+ ## ------------------------------------------------------------------ ##
+
+ #' @description Read stderr of the last process.
+ read_error = function(n = -1) {
+ private$last()$read_error(n)
+ },
+
+ #' @description Read stderr lines of the last process.
+ read_error_lines = function(n = -1) {
+ private$last()$read_error_lines(n)
+ },
+
+ #' @description Read all stderr of the last process.
+ read_all_error = function() {
+ private$last()$read_all_error()
+ },
+
+ #' @description Read all stderr lines of the last process.
+ read_all_error_lines = function() {
+ private$last()$read_all_error_lines()
+ },
+
+ ## ------------------------------------------------------------------ ##
+ ## Input (first process) ##
+ ## ------------------------------------------------------------------ ##
+
+ #' @description Write to the first process stdin.
+ write_input = function(str, sep = "\n") {
+ private$procs[[1L]]$write_input(str, sep)
+ },
+
+ #' @description Close the first process stdin (signals EOF to the process).
+ close_input = function() {
+ close(private$procs[[1L]]$get_input_connection())
+ },
+
+ ## ------------------------------------------------------------------ ##
+ ## Lifecycle ##
+ ## ------------------------------------------------------------------ ##
+
+ #' @description Wait for all processes to finish.
+ wait = function(timeout = -1) {
+ ## Wait for the last process first: it consumes the pipeline output.
+ ## Then wait for the rest in reverse order.
+ for (p in rev(private$procs)) {
+ p$wait(timeout)
+ }
+ invisible(self)
+ },
+
+ #' @description Kill all processes.
+ kill = function(grace = 0.1, close_connections = TRUE) {
+ for (p in private$procs) p$kill(grace, close_connections)
+ invisible(self)
+ },
+
+ #' @description Kill all process trees.
+ kill_tree = function(grace = 0.1, close_connections = TRUE) {
+ for (p in private$procs) p$kill_tree(grace, close_connections)
+ invisible(self)
+ },
+
+ #' @description Check if any process is still alive.
+ is_alive = function() {
+ any(vapply(private$procs, function(p) p$is_alive(), logical(1L)))
+ },
+
+ ## ------------------------------------------------------------------ ##
+ ## Status / accessors ##
+ ## ------------------------------------------------------------------ ##
+
+ #' @description Return exit codes for all processes.
+ get_exit_statuses = function() {
+ lapply(private$procs, function(p) p$get_exit_status())
+ },
+
+ #' @description Return PIDs for all processes.
+ get_pids = function() {
+ vapply(private$procs, function(p) p$get_pid(), integer(1L))
+ },
+
+ #' @description Return the list of process objects.
+ get_processes = function() {
+ private$procs
+ },
+
+ #' @description Format the pipeline as a string.
+ format = function() pipeline_format(self, private),
+
+ #' @description Print the pipeline to the screen.
+ print = function(...) pipeline_print(self, private)
+ ),
+
+ private = list(
+ procs = NULL,
+ last = function() private$procs[[length(private$procs)]]
+ )
+)
diff --git a/R/print.R b/R/print.R
index 42039ac8..a7bf0362 100644
--- a/R/print.R
+++ b/R/print.R
@@ -24,3 +24,15 @@ process_print <- function(self, private) {
process_get_short_name <- function(self, private) {
basename(private$command)
}
+
+pipeline_format <- function(self, private) {
+ lines <- vapply(private$procs, function(p) {
+ sub("^PROCESS ", "| ", p$format())
+ }, character(1L))
+ paste0(c("PIPELINE\n", lines), collapse = "")
+}
+
+pipeline_print <- function(self, private) {
+ cat(pipeline_format(self, private))
+ invisible(self)
+}
diff --git a/_pkgdown.yml b/_pkgdown.yml
index 0526a06c..a4033164 100644
--- a/_pkgdown.yml
+++ b/_pkgdown.yml
@@ -27,6 +27,10 @@ reference:
contents:
- process
+- title: Pipelines
+ contents:
+ - pipeline
+
- title: Polling
contents:
- poll
diff --git a/man/curl_fds.Rd b/man/curl_fds.Rd
index 3d522a34..febe9b09 100644
--- a/man/curl_fds.Rd
+++ b/man/curl_fds.Rd
@@ -8,7 +8,7 @@ curl_fds(fds)
}
\arguments{
\item{fds}{A list of file descriptors, as returned by
-\code{\link[curl:multi_fdset]{curl::multi_fdset()}}.}
+\code{\link[curl:multi]{curl::multi_fdset()}}.}
}
\value{
Pollable object, that be used with \code{\link[=poll]{poll()}} directly.
diff --git a/man/pipeline.Rd b/man/pipeline.Rd
new file mode 100644
index 00000000..a904459b
--- /dev/null
+++ b/man/pipeline.Rd
@@ -0,0 +1,430 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/pipeline.R
+\name{pipeline}
+\alias{pipeline}
+\title{Pipeline of processes connected with pipes}
+\description{
+\ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#experimental}{\figure{lifecycle-experimental.svg}{options: alt='[Experimental]'}}}{\strong{[Experimental]}}
+
+A \code{pipeline} object represents a sequence of processes whose standard
+input and output streams are connected with pipes, like a Unix pipeline
+(\code{cmd1 | cmd2 | cmd3}). Data flows directly between the child processes
+via kernel-level pipes — the parent R process sees only the output of the
+final command (when \code{stdout = "|"}).
+}
+\section{Methods}{
+
+\code{pipeline$new(cmds, stdin, stdout, stderr, env, encoding, wd, cleanup, cleanup_tree)}
+
+\verb{$read_output(n = -1)}, \verb{$read_output_lines(n = -1)},
+\verb{$read_all_output()}, \verb{$read_all_output_lines()} — read from the last
+process (only meaningful when \code{stdout = "|"}).
+
+\verb{$poll_io(timeout)} — poll the last process's connections for I/O.
+
+\verb{$read_error(n = -1)}, \verb{$read_error_lines(n = -1)},
+\verb{$read_all_error()}, \verb{$read_all_error_lines()} — read stderr of the
+last process (only meaningful when \code{stderr = "|"}).
+
+\verb{$write_input(str, sep = "\\n")} — write to first process stdin
+(only meaningful when \code{stdin = "|"}).
+
+\verb{$close_input()} — close the first process stdin, signalling EOF.
+
+\verb{$wait(timeout = -1)} — wait for all processes to finish.
+
+\verb{$kill(grace = 0.1, close_connections = TRUE)} — kill all processes.
+
+\verb{$kill_tree(grace = 0.1, close_connections = TRUE)} — kill all
+process trees.
+
+\verb{$is_alive()} — returns \code{TRUE} if any process is still running.
+
+\verb{$get_exit_statuses()} — list of exit codes (one per process; \code{NULL}
+if still running).
+
+\verb{$get_pids()} — integer vector of process IDs.
+
+\verb{$get_processes()} — list of \link{process} objects, one per command.
+
+\verb{$format()} — string representation of the pipeline.
+
+\verb{$print()} — print the pipeline to the screen.
+}
+
+\examples{
+\dontrun{
+# sort | uniq, reading from / writing to R
+pl <- pipeline$new(
+ list(c("sort"), c("uniq")),
+ stdin = "|", stdout = "|"
+)
+pl$write_input("b\na\nb\na\n")
+pl$close_input()
+pl$read_all_output_lines()
+pl$wait()
+pl$get_exit_statuses()
+}
+
+}
+\section{Methods}{
+\subsection{Public methods}{
+\itemize{
+\item \href{#method-pipeline-new}{\code{pipeline$new()}}
+\item \href{#method-pipeline-read_output}{\code{pipeline$read_output()}}
+\item \href{#method-pipeline-read_output_lines}{\code{pipeline$read_output_lines()}}
+\item \href{#method-pipeline-read_all_output}{\code{pipeline$read_all_output()}}
+\item \href{#method-pipeline-read_all_output_lines}{\code{pipeline$read_all_output_lines()}}
+\item \href{#method-pipeline-poll_io}{\code{pipeline$poll_io()}}
+\item \href{#method-pipeline-read_error}{\code{pipeline$read_error()}}
+\item \href{#method-pipeline-read_error_lines}{\code{pipeline$read_error_lines()}}
+\item \href{#method-pipeline-read_all_error}{\code{pipeline$read_all_error()}}
+\item \href{#method-pipeline-read_all_error_lines}{\code{pipeline$read_all_error_lines()}}
+\item \href{#method-pipeline-write_input}{\code{pipeline$write_input()}}
+\item \href{#method-pipeline-close_input}{\code{pipeline$close_input()}}
+\item \href{#method-pipeline-wait}{\code{pipeline$wait()}}
+\item \href{#method-pipeline-kill}{\code{pipeline$kill()}}
+\item \href{#method-pipeline-kill_tree}{\code{pipeline$kill_tree()}}
+\item \href{#method-pipeline-is_alive}{\code{pipeline$is_alive()}}
+\item \href{#method-pipeline-get_exit_statuses}{\code{pipeline$get_exit_statuses()}}
+\item \href{#method-pipeline-get_pids}{\code{pipeline$get_pids()}}
+\item \href{#method-pipeline-get_processes}{\code{pipeline$get_processes()}}
+\item \href{#method-pipeline-format}{\code{pipeline$format()}}
+\item \href{#method-pipeline-print}{\code{pipeline$print()}}
+}
+}
+\if{html}{\out{
}}
+\if{html}{\out{}}
+\if{latex}{\out{\hypertarget{method-pipeline-new}{}}}
+\subsection{Method \code{new()}}{
+Create a new pipeline.
+\subsection{Usage}{
+\if{html}{\out{}}\preformatted{pipeline$new(
+ cmds,
+ stdin = NULL,
+ stdout = "|",
+ stderr = NULL,
+ env = NULL,
+ encoding = "utf-8",
+ wd = NULL,
+ cleanup = TRUE,
+ cleanup_tree = FALSE
+)}\if{html}{\out{
}}
+}
+
+\subsection{Arguments}{
+\if{html}{\out{}}
+\describe{
+\item{\code{cmds}}{A non-empty list of character vectors. Each vector is one
+command: the first element is the executable and the rest are its
+arguments. Example: \code{list(c("sort"), c("uniq", "-c"))}.}
+
+\item{\code{stdin}}{Standard input for the \emph{first} process. \code{NULL} to discard,
+\code{"|"} so the parent R process can write to it via \verb{$write_input()}, or
+a file path.}
+
+\item{\code{stdout}}{Standard output of the \emph{last} process. \code{"|"} (the default)
+so the parent R process can read from it, \code{NULL} to discard, or a file
+path.}
+
+\item{\code{stderr}}{Standard error for \emph{all} processes. \code{NULL} (the default) to
+discard, \code{"|"} to create a separate readable pipe per process, \code{"2>&1"}
+to merge into stdout, or a file path. When \code{"|"}, use
+\verb{$read_error()} to read from the last process; use \verb{$get_processes()}
+to access individual process objects for other processes.}
+
+\item{\code{env}}{Environment variables for all processes, or \code{NULL} to inherit
+the parent environment.}
+
+\item{\code{encoding}}{Assumed encoding for stdin/stdout/stderr streams.}
+
+\item{\code{wd}}{Working directory for all processes, or \code{NULL} for the current
+directory.}
+
+\item{\code{cleanup}}{Whether to kill the processes on garbage collection.}
+
+\item{\code{cleanup_tree}}{Whether to kill the full process trees on garbage
+collection.}
+}
+\if{html}{\out{
}}
+}
+}
+\if{html}{\out{
}}
+\if{html}{\out{}}
+\if{latex}{\out{\hypertarget{method-pipeline-read_output}{}}}
+\subsection{Method \code{read_output()}}{
+Read output of the last process.
+\subsection{Usage}{
+\if{html}{\out{}}\preformatted{pipeline$read_output(n = -1)}\if{html}{\out{
}}
+}
+
+\subsection{Arguments}{
+\if{html}{\out{}}
+\describe{
+\item{\code{n}}{Number of characters or lines to read. -1 means all available.}
+}
+\if{html}{\out{
}}
+}
+}
+\if{html}{\out{
}}
+\if{html}{\out{}}
+\if{latex}{\out{\hypertarget{method-pipeline-read_output_lines}{}}}
+\subsection{Method \code{read_output_lines()}}{
+Read output lines of the last process.
+\subsection{Usage}{
+\if{html}{\out{}}\preformatted{pipeline$read_output_lines(n = -1)}\if{html}{\out{
}}
+}
+
+\subsection{Arguments}{
+\if{html}{\out{}}
+\describe{
+\item{\code{n}}{Number of characters or lines to read. -1 means all available.}
+}
+\if{html}{\out{
}}
+}
+}
+\if{html}{\out{
}}
+\if{html}{\out{}}
+\if{latex}{\out{\hypertarget{method-pipeline-read_all_output}{}}}
+\subsection{Method \code{read_all_output()}}{
+Read all output of the last process.
+\subsection{Usage}{
+\if{html}{\out{}}\preformatted{pipeline$read_all_output()}\if{html}{\out{
}}
+}
+
+}
+\if{html}{\out{
}}
+\if{html}{\out{}}
+\if{latex}{\out{\hypertarget{method-pipeline-read_all_output_lines}{}}}
+\subsection{Method \code{read_all_output_lines()}}{
+Read all output lines of the last process.
+\subsection{Usage}{
+\if{html}{\out{}}\preformatted{pipeline$read_all_output_lines()}\if{html}{\out{
}}
+}
+
+}
+\if{html}{\out{
}}
+\if{html}{\out{}}
+\if{latex}{\out{\hypertarget{method-pipeline-poll_io}{}}}
+\subsection{Method \code{poll_io()}}{
+Poll the connections of the last process for I/O.
+\subsection{Usage}{
+\if{html}{\out{}}\preformatted{pipeline$poll_io(timeout)}\if{html}{\out{
}}
+}
+
+\subsection{Arguments}{
+\if{html}{\out{}}
+\describe{
+\item{\code{timeout}}{Timeout in milliseconds. -1 means no timeout.}
+}
+\if{html}{\out{
}}
+}
+}
+\if{html}{\out{
}}
+\if{html}{\out{}}
+\if{latex}{\out{\hypertarget{method-pipeline-read_error}{}}}
+\subsection{Method \code{read_error()}}{
+Read stderr of the last process.
+\subsection{Usage}{
+\if{html}{\out{}}\preformatted{pipeline$read_error(n = -1)}\if{html}{\out{
}}
+}
+
+\subsection{Arguments}{
+\if{html}{\out{}}
+\describe{
+\item{\code{n}}{Number of characters or lines to read. -1 means all available.}
+}
+\if{html}{\out{
}}
+}
+}
+\if{html}{\out{
}}
+\if{html}{\out{}}
+\if{latex}{\out{\hypertarget{method-pipeline-read_error_lines}{}}}
+\subsection{Method \code{read_error_lines()}}{
+Read stderr lines of the last process.
+\subsection{Usage}{
+\if{html}{\out{}}\preformatted{pipeline$read_error_lines(n = -1)}\if{html}{\out{
}}
+}
+
+\subsection{Arguments}{
+\if{html}{\out{}}
+\describe{
+\item{\code{n}}{Number of characters or lines to read. -1 means all available.}
+}
+\if{html}{\out{
}}
+}
+}
+\if{html}{\out{
}}
+\if{html}{\out{}}
+\if{latex}{\out{\hypertarget{method-pipeline-read_all_error}{}}}
+\subsection{Method \code{read_all_error()}}{
+Read all stderr of the last process.
+\subsection{Usage}{
+\if{html}{\out{}}\preformatted{pipeline$read_all_error()}\if{html}{\out{
}}
+}
+
+}
+\if{html}{\out{
}}
+\if{html}{\out{}}
+\if{latex}{\out{\hypertarget{method-pipeline-read_all_error_lines}{}}}
+\subsection{Method \code{read_all_error_lines()}}{
+Read all stderr lines of the last process.
+\subsection{Usage}{
+\if{html}{\out{}}\preformatted{pipeline$read_all_error_lines()}\if{html}{\out{
}}
+}
+
+}
+\if{html}{\out{
}}
+\if{html}{\out{}}
+\if{latex}{\out{\hypertarget{method-pipeline-write_input}{}}}
+\subsection{Method \code{write_input()}}{
+Write to the first process stdin.
+\subsection{Usage}{
+\if{html}{\out{}}\preformatted{pipeline$write_input(str, sep = "\\n")}\if{html}{\out{
}}
+}
+
+\subsection{Arguments}{
+\if{html}{\out{}}
+\describe{
+\item{\code{str}}{String to write to the process stdin.}
+
+\item{\code{sep}}{Separator to add after \code{str}.}
+}
+\if{html}{\out{
}}
+}
+}
+\if{html}{\out{
}}
+\if{html}{\out{}}
+\if{latex}{\out{\hypertarget{method-pipeline-close_input}{}}}
+\subsection{Method \code{close_input()}}{
+Close the first process stdin (signals EOF to the process).
+\subsection{Usage}{
+\if{html}{\out{}}\preformatted{pipeline$close_input()}\if{html}{\out{
}}
+}
+
+}
+\if{html}{\out{
}}
+\if{html}{\out{}}
+\if{latex}{\out{\hypertarget{method-pipeline-wait}{}}}
+\subsection{Method \code{wait()}}{
+Wait for all processes to finish.
+\subsection{Usage}{
+\if{html}{\out{}}\preformatted{pipeline$wait(timeout = -1)}\if{html}{\out{
}}
+}
+
+\subsection{Arguments}{
+\if{html}{\out{}}
+\describe{
+\item{\code{timeout}}{Timeout in milliseconds. -1 means no timeout.}
+}
+\if{html}{\out{
}}
+}
+}
+\if{html}{\out{
}}
+\if{html}{\out{}}
+\if{latex}{\out{\hypertarget{method-pipeline-kill}{}}}
+\subsection{Method \code{kill()}}{
+Kill all processes.
+\subsection{Usage}{
+\if{html}{\out{}}\preformatted{pipeline$kill(grace = 0.1, close_connections = TRUE)}\if{html}{\out{
}}
+}
+
+\subsection{Arguments}{
+\if{html}{\out{}}
+\describe{
+\item{\code{grace}}{Grace period in seconds before sending SIGKILL (Unix) or
+terminating forcefully (Windows). Currently not used.}
+
+\item{\code{close_connections}}{Whether to close connections after killing.}
+}
+\if{html}{\out{
}}
+}
+}
+\if{html}{\out{
}}
+\if{html}{\out{}}
+\if{latex}{\out{\hypertarget{method-pipeline-kill_tree}{}}}
+\subsection{Method \code{kill_tree()}}{
+Kill all process trees.
+\subsection{Usage}{
+\if{html}{\out{}}\preformatted{pipeline$kill_tree(grace = 0.1, close_connections = TRUE)}\if{html}{\out{
}}
+}
+
+\subsection{Arguments}{
+\if{html}{\out{}}
+\describe{
+\item{\code{grace}}{Grace period in seconds before sending SIGKILL (Unix) or
+terminating forcefully (Windows). Currently not used.}
+
+\item{\code{close_connections}}{Whether to close connections after killing.}
+}
+\if{html}{\out{
}}
+}
+}
+\if{html}{\out{
}}
+\if{html}{\out{}}
+\if{latex}{\out{\hypertarget{method-pipeline-is_alive}{}}}
+\subsection{Method \code{is_alive()}}{
+Check if any process is still alive.
+\subsection{Usage}{
+\if{html}{\out{}}\preformatted{pipeline$is_alive()}\if{html}{\out{
}}
+}
+
+}
+\if{html}{\out{
}}
+\if{html}{\out{}}
+\if{latex}{\out{\hypertarget{method-pipeline-get_exit_statuses}{}}}
+\subsection{Method \code{get_exit_statuses()}}{
+Return exit codes for all processes.
+\subsection{Usage}{
+\if{html}{\out{}}\preformatted{pipeline$get_exit_statuses()}\if{html}{\out{
}}
+}
+
+}
+\if{html}{\out{
}}
+\if{html}{\out{}}
+\if{latex}{\out{\hypertarget{method-pipeline-get_pids}{}}}
+\subsection{Method \code{get_pids()}}{
+Return PIDs for all processes.
+\subsection{Usage}{
+\if{html}{\out{}}\preformatted{pipeline$get_pids()}\if{html}{\out{
}}
+}
+
+}
+\if{html}{\out{
}}
+\if{html}{\out{}}
+\if{latex}{\out{\hypertarget{method-pipeline-get_processes}{}}}
+\subsection{Method \code{get_processes()}}{
+Return the list of process objects.
+\subsection{Usage}{
+\if{html}{\out{}}\preformatted{pipeline$get_processes()}\if{html}{\out{
}}
+}
+
+}
+\if{html}{\out{
}}
+\if{html}{\out{}}
+\if{latex}{\out{\hypertarget{method-pipeline-format}{}}}
+\subsection{Method \code{format()}}{
+Format the pipeline as a string.
+\subsection{Usage}{
+\if{html}{\out{}}\preformatted{pipeline$format()}\if{html}{\out{
}}
+}
+
+}
+\if{html}{\out{
}}
+\if{html}{\out{}}
+\if{latex}{\out{\hypertarget{method-pipeline-print}{}}}
+\subsection{Method \code{print()}}{
+Print the pipeline to the screen.
+\subsection{Usage}{
+\if{html}{\out{}}\preformatted{pipeline$print(...)}\if{html}{\out{
}}
+}
+
+\subsection{Arguments}{
+\if{html}{\out{}}
+\describe{
+\item{\code{...}}{Not used, for compatibility with the generic.}
+}
+\if{html}{\out{
}}
+}
+}
+}
diff --git a/man/process.Rd b/man/process.Rd
index 51234e3c..1a6ce99a 100644
--- a/man/process.Rd
+++ b/man/process.Rd
@@ -131,70 +131,69 @@ p$is_alive()
}
\section{Methods}{
\subsection{Public methods}{
- \itemize{
- \item \href{#method-process-initialize}{\code{process$new()}}
- \item \href{#method-process-kill}{\code{process$kill()}}
- \item \href{#method-process-kill_tree}{\code{process$kill_tree()}}
- \item \href{#method-process-signal}{\code{process$signal()}}
- \item \href{#method-process-interrupt}{\code{process$interrupt()}}
- \item \href{#method-process-get_pid}{\code{process$get_pid()}}
- \item \href{#method-process-is_alive}{\code{process$is_alive()}}
- \item \href{#method-process-wait}{\code{process$wait()}}
- \item \href{#method-process-get_exit_status}{\code{process$get_exit_status()}}
- \item \href{#method-process-format}{\code{process$format()}}
- \item \href{#method-process-print}{\code{process$print()}}
- \item \href{#method-process-get_start_time}{\code{process$get_start_time()}}
- \item \href{#method-process-get_end_time}{\code{process$get_end_time()}}
- \item \href{#method-process-is_supervised}{\code{process$is_supervised()}}
- \item \href{#method-process-supervise}{\code{process$supervise()}}
- \item \href{#method-process-read_output}{\code{process$read_output()}}
- \item \href{#method-process-read_error}{\code{process$read_error()}}
- \item \href{#method-process-read_output_bytes}{\code{process$read_output_bytes()}}
- \item \href{#method-process-read_error_bytes}{\code{process$read_error_bytes()}}
- \item \href{#method-process-read_output_lines}{\code{process$read_output_lines()}}
- \item \href{#method-process-read_error_lines}{\code{process$read_error_lines()}}
- \item \href{#method-process-is_incomplete_output}{\code{process$is_incomplete_output()}}
- \item \href{#method-process-is_incomplete_error}{\code{process$is_incomplete_error()}}
- \item \href{#method-process-has_input_connection}{\code{process$has_input_connection()}}
- \item \href{#method-process-has_output_connection}{\code{process$has_output_connection()}}
- \item \href{#method-process-has_error_connection}{\code{process$has_error_connection()}}
- \item \href{#method-process-has_poll_connection}{\code{process$has_poll_connection()}}
- \item \href{#method-process-get_input_connection}{\code{process$get_input_connection()}}
- \item \href{#method-process-get_output_connection}{\code{process$get_output_connection()}}
- \item \href{#method-process-get_error_connection}{\code{process$get_error_connection()}}
- \item \href{#method-process-read_all_output}{\code{process$read_all_output()}}
- \item \href{#method-process-read_all_error}{\code{process$read_all_error()}}
- \item \href{#method-process-read_all_output_lines}{\code{process$read_all_output_lines()}}
- \item \href{#method-process-read_all_error_lines}{\code{process$read_all_error_lines()}}
- \item \href{#method-process-write_input}{\code{process$write_input()}}
- \item \href{#method-process-get_input_file}{\code{process$get_input_file()}}
- \item \href{#method-process-get_output_file}{\code{process$get_output_file()}}
- \item \href{#method-process-get_error_file}{\code{process$get_error_file()}}
- \item \href{#method-process-poll_io}{\code{process$poll_io()}}
- \item \href{#method-process-get_poll_connection}{\code{process$get_poll_connection()}}
- \item \href{#method-process-get_result}{\code{process$get_result()}}
- \item \href{#method-process-as_ps_handle}{\code{process$as_ps_handle()}}
- \item \href{#method-process-get_name}{\code{process$get_name()}}
- \item \href{#method-process-get_exe}{\code{process$get_exe()}}
- \item \href{#method-process-get_cmdline}{\code{process$get_cmdline()}}
- \item \href{#method-process-get_status}{\code{process$get_status()}}
- \item \href{#method-process-get_username}{\code{process$get_username()}}
- \item \href{#method-process-get_wd}{\code{process$get_wd()}}
- \item \href{#method-process-get_cpu_times}{\code{process$get_cpu_times()}}
- \item \href{#method-process-get_memory_info}{\code{process$get_memory_info()}}
- \item \href{#method-process-suspend}{\code{process$suspend()}}
- \item \href{#method-process-resume}{\code{process$resume()}}
- \item \href{#method-process-clone}{\code{process$clone()}}
- }
+\itemize{
+\item \href{#method-process-new}{\code{process$new()}}
+\item \href{#method-process-kill}{\code{process$kill()}}
+\item \href{#method-process-kill_tree}{\code{process$kill_tree()}}
+\item \href{#method-process-signal}{\code{process$signal()}}
+\item \href{#method-process-interrupt}{\code{process$interrupt()}}
+\item \href{#method-process-get_pid}{\code{process$get_pid()}}
+\item \href{#method-process-is_alive}{\code{process$is_alive()}}
+\item \href{#method-process-wait}{\code{process$wait()}}
+\item \href{#method-process-get_exit_status}{\code{process$get_exit_status()}}
+\item \href{#method-process-format}{\code{process$format()}}
+\item \href{#method-process-print}{\code{process$print()}}
+\item \href{#method-process-get_start_time}{\code{process$get_start_time()}}
+\item \href{#method-process-get_end_time}{\code{process$get_end_time()}}
+\item \href{#method-process-is_supervised}{\code{process$is_supervised()}}
+\item \href{#method-process-supervise}{\code{process$supervise()}}
+\item \href{#method-process-read_output}{\code{process$read_output()}}
+\item \href{#method-process-read_error}{\code{process$read_error()}}
+\item \href{#method-process-read_output_bytes}{\code{process$read_output_bytes()}}
+\item \href{#method-process-read_error_bytes}{\code{process$read_error_bytes()}}
+\item \href{#method-process-read_output_lines}{\code{process$read_output_lines()}}
+\item \href{#method-process-read_error_lines}{\code{process$read_error_lines()}}
+\item \href{#method-process-is_incomplete_output}{\code{process$is_incomplete_output()}}
+\item \href{#method-process-is_incomplete_error}{\code{process$is_incomplete_error()}}
+\item \href{#method-process-has_input_connection}{\code{process$has_input_connection()}}
+\item \href{#method-process-has_output_connection}{\code{process$has_output_connection()}}
+\item \href{#method-process-has_error_connection}{\code{process$has_error_connection()}}
+\item \href{#method-process-has_poll_connection}{\code{process$has_poll_connection()}}
+\item \href{#method-process-get_input_connection}{\code{process$get_input_connection()}}
+\item \href{#method-process-get_output_connection}{\code{process$get_output_connection()}}
+\item \href{#method-process-get_error_connection}{\code{process$get_error_connection()}}
+\item \href{#method-process-read_all_output}{\code{process$read_all_output()}}
+\item \href{#method-process-read_all_error}{\code{process$read_all_error()}}
+\item \href{#method-process-read_all_output_lines}{\code{process$read_all_output_lines()}}
+\item \href{#method-process-read_all_error_lines}{\code{process$read_all_error_lines()}}
+\item \href{#method-process-write_input}{\code{process$write_input()}}
+\item \href{#method-process-get_input_file}{\code{process$get_input_file()}}
+\item \href{#method-process-get_output_file}{\code{process$get_output_file()}}
+\item \href{#method-process-get_error_file}{\code{process$get_error_file()}}
+\item \href{#method-process-poll_io}{\code{process$poll_io()}}
+\item \href{#method-process-get_poll_connection}{\code{process$get_poll_connection()}}
+\item \href{#method-process-get_result}{\code{process$get_result()}}
+\item \href{#method-process-as_ps_handle}{\code{process$as_ps_handle()}}
+\item \href{#method-process-get_name}{\code{process$get_name()}}
+\item \href{#method-process-get_exe}{\code{process$get_exe()}}
+\item \href{#method-process-get_cmdline}{\code{process$get_cmdline()}}
+\item \href{#method-process-get_status}{\code{process$get_status()}}
+\item \href{#method-process-get_username}{\code{process$get_username()}}
+\item \href{#method-process-get_wd}{\code{process$get_wd()}}
+\item \href{#method-process-get_cpu_times}{\code{process$get_cpu_times()}}
+\item \href{#method-process-get_memory_info}{\code{process$get_memory_info()}}
+\item \href{#method-process-suspend}{\code{process$suspend()}}
+\item \href{#method-process-resume}{\code{process$resume()}}
+\item \href{#method-process-clone}{\code{process$clone()}}
+}
}
\if{html}{\out{
}}
-\if{html}{\out{}}
-\if{latex}{\out{\hypertarget{method-process-initialize}{}}}
-\subsection{\code{process$new()}}{
- Start a new process in the background, and then return immediately.
- \subsection{Usage}{
- \if{html}{\out{}}
- \preformatted{process$new(
+\if{html}{\out{
}}
+\if{latex}{\out{\hypertarget{method-process-new}{}}}
+\subsection{Method \code{new()}}{
+Start a new process in the background, and then return immediately.
+\subsection{Usage}{
+\if{html}{\out{
}}\preformatted{process$new(
command = NULL,
args = character(),
stdin = NULL,
@@ -216,23 +215,25 @@ p$is_alive()
encoding = "",
post_process = NULL,
linux_pdeathsig = FALSE
-)}
- \if{html}{\out{
}}
- }
- \subsection{Arguments}{
- \if{html}{\out{
}}
- \describe{
- \item{\code{command}}{Character scalar, the command to run.
+)}\if{html}{\out{
}}
+}
+
+\subsection{Arguments}{
+\if{html}{\out{
}}
+\describe{
+\item{\code{command}}{Character scalar, the command to run.
Note that this argument is not passed to a shell, so no
tilde-expansion or variable substitution is performed on it.
It should not be quoted with \code{\link[base:shQuote]{base::shQuote()}}. See
\code{\link[base:normalizePath]{base::normalizePath()}} for tilde-expansion. If you want to run
\code{.bat} or \code{.cmd} files on Windows, make sure you read the
'Batch files' section above.}
- \item{\code{args}}{Character vector, arguments to the command. They will be
+
+\item{\code{args}}{Character vector, arguments to the command. They will be
passed to the process as is, without a shell transforming them,
They don't need to be escaped.}
- \item{\code{stdin}}{What to do with the standard input. Possible values:
+
+\item{\code{stdin}}{What to do with the standard input. Possible values:
\itemize{
\item \code{NULL}: set to the \emph{null device}, i.e. no standard input is
provided;
@@ -242,7 +243,8 @@ provided;
main R process does not have a standard input stream, e.g. in
RGui on Windows, then an error is thrown.
}}
- \item{\code{stdout}}{What to do with the standard output. Possible values:
+
+\item{\code{stdout}}{What to do with the standard output. Possible values:
\itemize{
\item \code{NULL}: discard it;
\item A string starting with \code{">>"}, e.g. \code{">>output.txt"}: append it
@@ -257,7 +259,8 @@ directory in the \code{wd} argument. (See issue 324.)
main R process does not have a standard output stream, e.g. in
RGui on Windows, then an error is thrown.
}}
- \item{\code{stderr}}{What to do with the standard error. Possible values:
+
+\item{\code{stderr}}{What to do with the standard error. Possible values:
\itemize{
\item \code{NULL}: discard it.
\item A string starting with \code{">>"}, e.g. \code{">>error.txt"}: append it
@@ -275,7 +278,8 @@ correctly interleaved.
main R process does not have a standard error stream, e.g. in
RGui on Windows, then an error is thrown.
}}
- \item{\code{pty}}{Whether to create a pseudo terminal (pty) for the
+
+\item{\code{pty}}{Whether to create a pseudo terminal (pty) for the
background process. This is currently only supported on Unix
systems, but not supported on Solaris.
If it is \code{TRUE}, then the \code{stdin}, \code{stdout} and \code{stderr} arguments
@@ -289,11 +293,14 @@ the \verb{$read_output()} method. Also, because \verb{$read_output_lines()}
could still block if no complete line is available, this function
always fails if the process has a pty. Use \verb{$read_output()} to
read from ptys.}
- \item{\code{pty_options}}{Unix pseudo terminal options, a named list. see
+
+\item{\code{pty_options}}{Unix pseudo terminal options, a named list. see
\code{\link[=default_pty_options]{default_pty_options()}} for details and defaults.}
- \item{\code{connections}}{A list of processx connections to pass to the
+
+\item{\code{connections}}{A list of processx connections to pass to the
child process. This is an experimental feature currently.}
- \item{\code{poll_connection}}{Whether to create an extra connection to the
+
+\item{\code{poll_connection}}{Whether to create an extra connection to the
process that allows polling, even if the standard input and
standard output are not pipes. If this is \code{NULL} (the default),
then this connection will be only created if standard output and
@@ -303,7 +310,8 @@ If the poll connection is created, you can query it via
to \code{p$poll_io()} and \code{\link[=poll]{poll()}}. The numeric file descriptor of the
poll connection comes right after \code{stderr} (2), and the
connections listed in \code{connections}.}
- \item{\code{env}}{Environment variables of the child process. If \code{NULL},
+
+\item{\code{env}}{Environment variables of the child process. If \code{NULL},
the parent's environment is inherited. On Windows, many programs
cannot function correctly if some environment variables are not
set, so we always set \code{HOMEDRIVE}, \code{HOMEPATH}, \code{LOGONSERVER},
@@ -312,26 +320,35 @@ set, so we always set \code{HOMEDRIVE}, \code{HOMEPATH}, \code{LOGONSERVER},
variables to the ones set in the current process, specify
\code{"current"} in \code{env}, without a name, and the appended ones with
names. The appended ones can overwrite the current ones.}
- \item{\code{cleanup}}{Whether to kill the process when the \code{process}
+
+\item{\code{cleanup}}{Whether to kill the process when the \code{process}
object is garbage collected.}
- \item{\code{cleanup_tree}}{Whether to kill the process and its child
+
+\item{\code{cleanup_tree}}{Whether to kill the process and its child
process tree when the \code{process} object is garbage collected.}
- \item{\code{wd}}{Working directory of the process. It must exist.
+
+\item{\code{wd}}{Working directory of the process. It must exist.
If \code{NULL}, then the current working directory is used.}
- \item{\code{echo_cmd}}{Whether to print the command to the screen before
+
+\item{\code{echo_cmd}}{Whether to print the command to the screen before
running it.}
- \item{\code{supervise}}{Whether to register the process with a supervisor.
+
+\item{\code{supervise}}{Whether to register the process with a supervisor.
If \code{TRUE}, the supervisor will ensure that the process is
killed when the R process exits.}
- \item{\code{windows_verbatim_args}}{Whether to omit quoting the arguments
+
+\item{\code{windows_verbatim_args}}{Whether to omit quoting the arguments
on Windows. It is ignored on other platforms.}
- \item{\code{windows_hide_window}}{Whether to hide the application's window
+
+\item{\code{windows_hide_window}}{Whether to hide the application's window
on Windows. It is ignored on other platforms.}
- \item{\code{windows_detached_process}}{Whether to use the
+
+\item{\code{windows_detached_process}}{Whether to use the
\code{DETACHED_PROCESS} flag on Windows. If this is \code{TRUE}, then
the child process will have no attached console, even if the
parent had one.}
- \item{\code{encoding}}{The encoding to assume for \code{stdin}, \code{stdout} and
+
+\item{\code{encoding}}{The encoding to assume for \code{stdin}, \code{stdout} and
\code{stderr}. By default the encoding of the current locale is
used. Note that \code{processx} always reencodes the output of the
\code{stdout} and \code{stderr} streams in UTF-8 currently.
@@ -340,53 +357,53 @@ specify \code{"UTF-8"} as encoding. Use \code{"binary"} to disable text
conversion entirely: \verb{$read_output()} and \verb{$read_error()} will
return raw vectors instead of character strings, preserving all
bytes including null bytes and non-UTF-8 byte sequences.}
- \item{\code{post_process}}{An optional function to run when the process has
+
+\item{\code{post_process}}{An optional function to run when the process has
finished. Currently it only runs if \verb{$get_result()} is called.
It is only run once.}
- \item{\code{linux_pdeathsig}}{On Linux, send this signal to the child process
+
+\item{\code{linux_pdeathsig}}{On Linux, send this signal to the child process
when the parent R process exits. \code{FALSE} (the default) disables this.
\code{TRUE} sends \code{SIGTERM}. An integer signal number, e.g.
\code{tools::SIGTERM} or \code{tools::SIGKILL}, sends that signal. Ignored on
non-Linux platforms.}
- }
- \if{html}{\out{
}}
- }
- \subsection{Returns}{
- R6 object representing the process.
- }
}
-
+\if{html}{\out{
}}
+}
+\subsection{Returns}{
+R6 object representing the process.
+}
+}
\if{html}{\out{
}}
\if{html}{\out{}}
\if{latex}{\out{\hypertarget{method-process-kill}{}}}
-\subsection{\code{process$kill()}}{
- Terminate the process. It also terminate all of its child
+\subsection{Method \code{kill()}}{
+Terminate the process. It also terminate all of its child
processes, except if they have created a new process group (on Unix),
or job object (on Windows). It returns \code{TRUE} if the process
was terminated, and \code{FALSE} if it was not (because it was
already finished/dead when \code{processx} tried to terminate it).
- \subsection{Usage}{
- \if{html}{\out{}}
- \preformatted{process$kill(grace = 0.1, close_connections = TRUE)}
- \if{html}{\out{
}}
- }
- \subsection{Arguments}{
- \if{html}{\out{}}
- \describe{
- \item{\code{grace}}{Currently not used.}
- \item{\code{close_connections}}{Whether to close standard input, standard
+\subsection{Usage}{
+\if{html}{\out{
}}\preformatted{process$kill(grace = 0.1, close_connections = TRUE)}\if{html}{\out{
}}
+}
+
+\subsection{Arguments}{
+\if{html}{\out{
}}
+\describe{
+\item{\code{grace}}{Currently not used.}
+
+\item{\code{close_connections}}{Whether to close standard input, standard
output, standard error connections and the poll connection, after
killing the process.}
- }
- \if{html}{\out{
}}
- }
}
-
+\if{html}{\out{
}}
+}
+}
\if{html}{\out{
}}
\if{html}{\out{}}
\if{latex}{\out{\hypertarget{method-process-kill_tree}{}}}
-\subsection{\code{process$kill_tree()}}{
- Process tree cleanup. It terminates the process
+\subsection{Method \code{kill_tree()}}{
+Process tree cleanup. It terminates the process
(if still alive), together with any child (or grandchild, etc.)
processes. It uses the \emph{ps} package, so that needs to be installed,
and \emph{ps} needs to support the current platform as well. Process tree
@@ -397,98 +414,89 @@ to the root of the tree cleanup in the process tree any more.
\verb{$kill_tree()} returns a named integer vector of the process ids that
were killed, the names are the names of the processes (e.g. \code{"sleep"},
\code{"notepad.exe"}, \code{"Rterm.exe"}, etc.).
- \subsection{Usage}{
- \if{html}{\out{}}
- \preformatted{process$kill_tree(grace = 0.1, close_connections = TRUE)}
- \if{html}{\out{
}}
- }
- \subsection{Arguments}{
- \if{html}{\out{}}
- \describe{
- \item{\code{grace}}{Currently not used.}
- \item{\code{close_connections}}{Whether to close standard input, standard
+\subsection{Usage}{
+\if{html}{\out{
}}\preformatted{process$kill_tree(grace = 0.1, close_connections = TRUE)}\if{html}{\out{
}}
+}
+
+\subsection{Arguments}{
+\if{html}{\out{
}}
+\describe{
+\item{\code{grace}}{Currently not used.}
+
+\item{\code{close_connections}}{Whether to close standard input, standard
output, standard error connections and the poll connection, after
killing the process.}
- }
- \if{html}{\out{
}}
- }
}
-
+\if{html}{\out{
}}
+}
+}
\if{html}{\out{
}}
\if{html}{\out{}}
\if{latex}{\out{\hypertarget{method-process-signal}{}}}
-\subsection{\code{process$signal()}}{
- Send a signal to the process. On Windows only the
+\subsection{Method \code{signal()}}{
+Send a signal to the process. On Windows only the
\code{SIGINT}, \code{SIGTERM} and \code{SIGKILL} signals are interpreted,
and the special 0 signal. The first three all kill the process. The 0
signal returns \code{TRUE} if the process is alive, and \code{FALSE}
otherwise. On Unix all signals are supported that the OS supports,
and the 0 signal as well.
- \subsection{Usage}{
- \if{html}{\out{}}
- \preformatted{process$signal(signal)}
- \if{html}{\out{
}}
- }
- \subsection{Arguments}{
- \if{html}{\out{}}
- \describe{
- \item{\code{signal}}{An integer scalar, the id of the signal to send to
-the process. See \code{\link[tools:pskill]{tools::pskill()}} for the list of signals.}
- }
- \if{html}{\out{
}}
- }
+\subsection{Usage}{
+\if{html}{\out{}}\preformatted{process$signal(signal)}\if{html}{\out{
}}
}
+\subsection{Arguments}{
+\if{html}{\out{}}
+\describe{
+\item{\code{signal}}{An integer scalar, the id of the signal to send to
+the process. See \code{\link[tools:pskill]{tools::pskill()}} for the list of signals.}
+}
+\if{html}{\out{
}}
+}
+}
\if{html}{\out{
}}
\if{html}{\out{}}
\if{latex}{\out{\hypertarget{method-process-interrupt}{}}}
-\subsection{\code{process$interrupt()}}{
- Send an interrupt to the process. On Unix this is a
+\subsection{Method \code{interrupt()}}{
+Send an interrupt to the process. On Unix this is a
\code{SIGINT} signal, and it is usually equivalent to pressing CTRL+C at
the terminal prompt. On Windows, it is a CTRL+BREAK keypress.
Applications may catch these events. By default they will quit.
- \subsection{Usage}{
- \if{html}{\out{}}
- \preformatted{process$interrupt()}
- \if{html}{\out{
}}
- }
+\subsection{Usage}{
+\if{html}{\out{}}\preformatted{process$interrupt()}\if{html}{\out{
}}
}
+}
\if{html}{\out{
}}
\if{html}{\out{}}
\if{latex}{\out{\hypertarget{method-process-get_pid}{}}}
-\subsection{\code{process$get_pid()}}{
- Query the process id.
- \subsection{Usage}{
- \if{html}{\out{}}
- \preformatted{process$get_pid()}
- \if{html}{\out{
}}
- }
- \subsection{Returns}{
- Integer scalar, the process id of the process.
- }
+\subsection{Method \code{get_pid()}}{
+Query the process id.
+\subsection{Usage}{
+\if{html}{\out{}}\preformatted{process$get_pid()}\if{html}{\out{
}}
}
+\subsection{Returns}{
+Integer scalar, the process id of the process.
+}
+}
\if{html}{\out{
}}
\if{html}{\out{}}
\if{latex}{\out{\hypertarget{method-process-is_alive}{}}}
-\subsection{\code{process$is_alive()}}{
- Check if the process is alive.
- \subsection{Usage}{
- \if{html}{\out{}}
- \preformatted{process$is_alive()}
- \if{html}{\out{
}}
- }
- \subsection{Returns}{
- Logical scalar.
- }
+\subsection{Method \code{is_alive()}}{
+Check if the process is alive.
+\subsection{Usage}{
+\if{html}{\out{}}\preformatted{process$is_alive()}\if{html}{\out{
}}
}
+\subsection{Returns}{
+Logical scalar.
+}
+}
\if{html}{\out{
}}
\if{html}{\out{}}
\if{latex}{\out{\hypertarget{method-process-wait}{}}}
-\subsection{\code{process$wait()}}{
- Wait until the process finishes, or a timeout happens.
+\subsection{Method \code{wait()}}{
+Wait until the process finishes, or a timeout happens.
Note that if the process never finishes, and the timeout is infinite
(the default), then R will never regain control. In some rare cases,
\verb{$wait()} might take a bit longer than specified to time out. This
@@ -496,141 +504,125 @@ happens on Unix, when another package overwrites the processx
\code{SIGCHLD} signal handler, after the processx process has started.
One such package is parallel, if used with fork clusters, e.g.
through \code{parallel::mcparallel()}.
- \subsection{Usage}{
- \if{html}{\out{}}
- \preformatted{process$wait(timeout = -1)}
- \if{html}{\out{
}}
- }
- \subsection{Arguments}{
- \if{html}{\out{}}
- \describe{
- \item{\code{timeout}}{Timeout in milliseconds, for the wait or the I/O
-polling.}
- }
- \if{html}{\out{
}}
- }
- \subsection{Returns}{
- It returns the process itself, invisibly.
- }
+\subsection{Usage}{
+\if{html}{\out{}}\preformatted{process$wait(timeout = -1)}\if{html}{\out{
}}
}
+\subsection{Arguments}{
+\if{html}{\out{}}
+\describe{
+\item{\code{timeout}}{Timeout in milliseconds, for the wait or the I/O
+polling.}
+}
+\if{html}{\out{
}}
+}
+\subsection{Returns}{
+It returns the process itself, invisibly.
+}
+}
\if{html}{\out{
}}
\if{html}{\out{}}
\if{latex}{\out{\hypertarget{method-process-get_exit_status}{}}}
-\subsection{\code{process$get_exit_status()}}{
- \verb{$get_exit_status} returns the exit code of the process if it has
+\subsection{Method \code{get_exit_status()}}{
+\verb{$get_exit_status} returns the exit code of the process if it has
finished and \code{NULL} otherwise. On Unix, in some rare cases, the exit
status might be \code{NA}. This happens if another package (or R itself)
overwrites the processx \code{SIGCHLD} handler, after the processx process
has started. In these cases processx cannot determine the real exit
status of the process. One such package is parallel, if used with
fork clusters, e.g. through the \code{parallel::mcparallel()} function.
- \subsection{Usage}{
- \if{html}{\out{}}
- \preformatted{process$get_exit_status()}
- \if{html}{\out{
}}
- }
+\subsection{Usage}{
+\if{html}{\out{}}\preformatted{process$get_exit_status()}\if{html}{\out{
}}
}
+}
\if{html}{\out{
}}
\if{html}{\out{}}
\if{latex}{\out{\hypertarget{method-process-format}{}}}
-\subsection{\code{process$format()}}{
- \code{format(p)} or \code{p$format()} creates a string representation of the
+\subsection{Method \code{format()}}{
+\code{format(p)} or \code{p$format()} creates a string representation of the
process, usually for printing.
- \subsection{Usage}{
- \if{html}{\out{}}
- \preformatted{process$format()}
- \if{html}{\out{
}}
- }
+\subsection{Usage}{
+\if{html}{\out{}}\preformatted{process$format()}\if{html}{\out{
}}
}
+}
\if{html}{\out{
}}
\if{html}{\out{}}
\if{latex}{\out{\hypertarget{method-process-print}{}}}
-\subsection{\code{process$print()}}{
- \code{print(p)} or \code{p$print()} shows some information about the
+\subsection{Method \code{print()}}{
+\code{print(p)} or \code{p$print()} shows some information about the
process on the screen, whether it is running and it's process id, etc.
- \subsection{Usage}{
- \if{html}{\out{}}
- \preformatted{process$print()}
- \if{html}{\out{
}}
- }
+\subsection{Usage}{
+\if{html}{\out{}}\preformatted{process$print()}\if{html}{\out{
}}
}
+}
\if{html}{\out{
}}
\if{html}{\out{}}
\if{latex}{\out{\hypertarget{method-process-get_start_time}{}}}
-\subsection{\code{process$get_start_time()}}{
- \verb{$get_start_time()} returns the time when the process was
+\subsection{Method \code{get_start_time()}}{
+\verb{$get_start_time()} returns the time when the process was
started.
- \subsection{Usage}{
- \if{html}{\out{}}
- \preformatted{process$get_start_time()}
- \if{html}{\out{
}}
- }
+\subsection{Usage}{
+\if{html}{\out{}}\preformatted{process$get_start_time()}\if{html}{\out{
}}
}
+}
\if{html}{\out{
}}
\if{html}{\out{}}
\if{latex}{\out{\hypertarget{method-process-get_end_time}{}}}
-\subsection{\code{process$get_end_time()}}{
- \verb{$get_end_time()} returns the time when the process finished,
+\subsection{Method \code{get_end_time()}}{
+\verb{$get_end_time()} returns the time when the process finished,
or \code{NULL} if it is still running.
On Unix the timestamp is recorded when R first notices the exit
(via the \code{SIGCHLD} handler or a call to \verb{$is_alive()},
\verb{$get_exit_status()}, or \verb{$wait()}), so it may be slightly later
than the actual kernel exit time.
On Windows the exact kernel exit time is used.
- \subsection{Usage}{
- \if{html}{\out{}}
- \preformatted{process$get_end_time()}
- \if{html}{\out{
}}
- }
+\subsection{Usage}{
+\if{html}{\out{}}\preformatted{process$get_end_time()}\if{html}{\out{
}}
}
+}
\if{html}{\out{
}}
\if{html}{\out{}}
\if{latex}{\out{\hypertarget{method-process-is_supervised}{}}}
-\subsection{\code{process$is_supervised()}}{
- \verb{$is_supervised()} returns whether the process is being tracked by
+\subsection{Method \code{is_supervised()}}{
+\verb{$is_supervised()} returns whether the process is being tracked by
supervisor process.
- \subsection{Usage}{
- \if{html}{\out{}}
- \preformatted{process$is_supervised()}
- \if{html}{\out{
}}
- }
+\subsection{Usage}{
+\if{html}{\out{}}\preformatted{process$is_supervised()}\if{html}{\out{
}}
}
+}
\if{html}{\out{
}}
\if{html}{\out{}}
\if{latex}{\out{\hypertarget{method-process-supervise}{}}}
-\subsection{\code{process$supervise()}}{
- \verb{$supervise()} if passed \code{TRUE}, tells the supervisor to start
+\subsection{Method \code{supervise()}}{
+\verb{$supervise()} if passed \code{TRUE}, tells the supervisor to start
tracking the process. If \code{FALSE}, tells the supervisor to stop
tracking the process. Note that even if the supervisor is disabled
for a process, if it was started with \code{cleanup = TRUE}, the process
will still be killed when the object is garbage collected.
- \subsection{Usage}{
- \if{html}{\out{}}
- \preformatted{process$supervise(status)}
- \if{html}{\out{
}}
- }
- \subsection{Arguments}{
- \if{html}{\out{}}
- \describe{
- \item{\code{status}}{Whether to turn on of off the supervisor for this
-process.}
- }
- \if{html}{\out{
}}
- }
+\subsection{Usage}{
+\if{html}{\out{}}\preformatted{process$supervise(status)}\if{html}{\out{
}}
}
+\subsection{Arguments}{
+\if{html}{\out{}}
+\describe{
+\item{\code{status}}{Whether to turn on of off the supervisor for this
+process.}
+}
+\if{html}{\out{
}}
+}
+}
\if{html}{\out{
}}
\if{html}{\out{}}
\if{latex}{\out{\hypertarget{method-process-read_output}{}}}
-\subsection{\code{process$read_output()}}{
- \verb{$read_output()} reads from the standard output connection of the
+\subsection{Method \code{read_output()}}{
+\verb{$read_output()} reads from the standard output connection of the
process. If the standard output connection was not requested, then
then it returns an error. It uses a non-blocking text connection. This
will work only if \code{stdout="|"} was used. Otherwise, it will throw an
@@ -643,90 +635,82 @@ finite, and \verb{$read_output()} only returns what is already buffered.
Always call \verb{$poll_io()} (or \code{\link[=poll]{poll()}}) before reading to avoid
deadlocking when the child fills the pipe buffer (see the \emph{Polling}
section for details). To read \emph{all} output call \verb{$read_all_output()}.
- \subsection{Usage}{
- \if{html}{\out{}}
- \preformatted{process$read_output(n = -1)}
- \if{html}{\out{
}}
- }
- \subsection{Arguments}{
- \if{html}{\out{}}
- \describe{
- \item{\code{n}}{Number of characters or lines to read.}
- }
- \if{html}{\out{
}}
- }
+\subsection{Usage}{
+\if{html}{\out{}}\preformatted{process$read_output(n = -1)}\if{html}{\out{
}}
}
+\subsection{Arguments}{
+\if{html}{\out{}}
+\describe{
+\item{\code{n}}{Number of characters or lines to read.}
+}
+\if{html}{\out{
}}
+}
+}
\if{html}{\out{
}}
\if{html}{\out{}}
\if{latex}{\out{\hypertarget{method-process-read_error}{}}}
-\subsection{\code{process$read_error()}}{
- \verb{$read_error()} is similar to \verb{$read_output()}, but reads from the
+\subsection{Method \code{read_error()}}{
+\verb{$read_error()} is similar to \verb{$read_output()}, but reads from the
standard error stream. Returns a raw vector when
\code{encoding = "binary"} was used. The same polling requirement applies
as for \verb{$read_output()} (see the \emph{Polling} section).
- \subsection{Usage}{
- \if{html}{\out{}}
- \preformatted{process$read_error(n = -1)}
- \if{html}{\out{
}}
- }
- \subsection{Arguments}{
- \if{html}{\out{}}
- \describe{
- \item{\code{n}}{Number of characters or lines to read.}
- }
- \if{html}{\out{
}}
- }
+\subsection{Usage}{
+\if{html}{\out{}}\preformatted{process$read_error(n = -1)}\if{html}{\out{
}}
}
+\subsection{Arguments}{
+\if{html}{\out{}}
+\describe{
+\item{\code{n}}{Number of characters or lines to read.}
+}
+\if{html}{\out{
}}
+}
+}
\if{html}{\out{
}}
\if{html}{\out{}}
\if{latex}{\out{\hypertarget{method-process-read_output_bytes}{}}}
-\subsection{\code{process$read_output_bytes()}}{
- \verb{$read_output_bytes()} reads from the standard output connection of
+\subsection{Method \code{read_output_bytes()}}{
+\verb{$read_output_bytes()} reads from the standard output connection of
the process and returns the result as a raw vector, preserving all
bytes including null bytes and other binary data. Switches the
underlying connection to raw mode; do not mix with \verb{$read_output()}.
This will work only if \code{stdout="|"} was used.
- \subsection{Usage}{
- \if{html}{\out{}}
- \preformatted{process$read_output_bytes(n = -1)}
- \if{html}{\out{
}}
- }
- \subsection{Arguments}{
- \if{html}{\out{}}
- \describe{
- \item{\code{n}}{Number of characters or lines to read.}
- }
- \if{html}{\out{
}}
- }
+\subsection{Usage}{
+\if{html}{\out{}}\preformatted{process$read_output_bytes(n = -1)}\if{html}{\out{
}}
}
+\subsection{Arguments}{
+\if{html}{\out{}}
+\describe{
+\item{\code{n}}{Number of characters or lines to read.}
+}
+\if{html}{\out{
}}
+}
+}
\if{html}{\out{
}}
\if{html}{\out{}}
\if{latex}{\out{\hypertarget{method-process-read_error_bytes}{}}}
-\subsection{\code{process$read_error_bytes()}}{
- \verb{$read_error_bytes()} is similar to \verb{$read_output_bytes()}, but reads
+\subsection{Method \code{read_error_bytes()}}{
+\verb{$read_error_bytes()} is similar to \verb{$read_output_bytes()}, but reads
from the standard error stream.
- \subsection{Usage}{
- \if{html}{\out{}}
- \preformatted{process$read_error_bytes(n = -1)}
- \if{html}{\out{
}}
- }
- \subsection{Arguments}{
- \if{html}{\out{}}
- \describe{
- \item{\code{n}}{Number of characters or lines to read.}
- }
- \if{html}{\out{
}}
- }
+\subsection{Usage}{
+\if{html}{\out{}}\preformatted{process$read_error_bytes(n = -1)}\if{html}{\out{
}}
}
+\subsection{Arguments}{
+\if{html}{\out{}}
+\describe{
+\item{\code{n}}{Number of characters or lines to read.}
+}
+\if{html}{\out{
}}
+}
+}
\if{html}{\out{
}}
\if{html}{\out{}}
\if{latex}{\out{\hypertarget{method-process-read_output_lines}{}}}
-\subsection{\code{process$read_output_lines()}}{
- \verb{$read_output_lines()} reads lines from standard output connection
+\subsection{Method \code{read_output_lines()}}{
+\verb{$read_output_lines()} reads lines from standard output connection
of the process. If the standard output connection was not requested,
then it returns an error. It uses a non-blocking text connection.
This will work only if \code{stdout="|"} was used. Otherwise, it will
@@ -739,236 +723,206 @@ on Linux/macOS, ~76KB on Windows) or when the line is not yet
terminated. Always call \verb{$poll_io()} before reading to avoid
deadlocking (see the \emph{Polling} section), and use \verb{$read_output()}
when lines may be very long.
- \subsection{Usage}{
- \if{html}{\out{}}
- \preformatted{process$read_output_lines(n = -1)}
- \if{html}{\out{
}}
- }
- \subsection{Arguments}{
- \if{html}{\out{}}
- \describe{
- \item{\code{n}}{Number of characters or lines to read.}
- }
- \if{html}{\out{
}}
- }
+\subsection{Usage}{
+\if{html}{\out{}}\preformatted{process$read_output_lines(n = -1)}\if{html}{\out{
}}
}
+\subsection{Arguments}{
+\if{html}{\out{}}
+\describe{
+\item{\code{n}}{Number of characters or lines to read.}
+}
+\if{html}{\out{
}}
+}
+}
\if{html}{\out{
}}
\if{html}{\out{}}
\if{latex}{\out{\hypertarget{method-process-read_error_lines}{}}}
-\subsection{\code{process$read_error_lines()}}{
- \verb{$read_error_lines()} is similar to \verb{$read_output_lines}, but
+\subsection{Method \code{read_error_lines()}}{
+\verb{$read_error_lines()} is similar to \verb{$read_output_lines}, but
it reads from the standard error stream. The same polling requirement
applies (see the \emph{Polling} section).
- \subsection{Usage}{
- \if{html}{\out{}}
- \preformatted{process$read_error_lines(n = -1)}
- \if{html}{\out{
}}
- }
- \subsection{Arguments}{
- \if{html}{\out{}}
- \describe{
- \item{\code{n}}{Number of characters or lines to read.}
- }
- \if{html}{\out{
}}
- }
+\subsection{Usage}{
+\if{html}{\out{}}\preformatted{process$read_error_lines(n = -1)}\if{html}{\out{
}}
}
+\subsection{Arguments}{
+\if{html}{\out{}}
+\describe{
+\item{\code{n}}{Number of characters or lines to read.}
+}
+\if{html}{\out{
}}
+}
+}
\if{html}{\out{
}}
\if{html}{\out{}}
\if{latex}{\out{\hypertarget{method-process-is_incomplete_output}{}}}
-\subsection{\code{process$is_incomplete_output()}}{
- \verb{$is_incomplete_output()} return \code{FALSE} if the other end of
+\subsection{Method \code{is_incomplete_output()}}{
+\verb{$is_incomplete_output()} return \code{FALSE} if the other end of
the standard output connection was closed (most probably because the
process exited). It return \code{TRUE} otherwise.
- \subsection{Usage}{
- \if{html}{\out{}}
- \preformatted{process$is_incomplete_output()}
- \if{html}{\out{
}}
- }
+\subsection{Usage}{
+\if{html}{\out{}}\preformatted{process$is_incomplete_output()}\if{html}{\out{
}}
}
+}
\if{html}{\out{
}}
\if{html}{\out{}}
\if{latex}{\out{\hypertarget{method-process-is_incomplete_error}{}}}
-\subsection{\code{process$is_incomplete_error()}}{
- \verb{$is_incomplete_error()} return \code{FALSE} if the other end of
+\subsection{Method \code{is_incomplete_error()}}{
+\verb{$is_incomplete_error()} return \code{FALSE} if the other end of
the standard error connection was closed (most probably because the
process exited). It return \code{TRUE} otherwise.
- \subsection{Usage}{
- \if{html}{\out{}}
- \preformatted{process$is_incomplete_error()}
- \if{html}{\out{
}}
- }
+\subsection{Usage}{
+\if{html}{\out{}}\preformatted{process$is_incomplete_error()}\if{html}{\out{
}}
}
+}
\if{html}{\out{
}}
\if{html}{\out{}}
\if{latex}{\out{\hypertarget{method-process-has_input_connection}{}}}
-\subsection{\code{process$has_input_connection()}}{
- \verb{$has_input_connection()} return \code{TRUE} if there is a connection
+\subsection{Method \code{has_input_connection()}}{
+\verb{$has_input_connection()} return \code{TRUE} if there is a connection
object for standard input; in other words, if \code{stdout="|"}. It returns
\code{FALSE} otherwise.
- \subsection{Usage}{
- \if{html}{\out{}}
- \preformatted{process$has_input_connection()}
- \if{html}{\out{
}}
- }
+\subsection{Usage}{
+\if{html}{\out{}}\preformatted{process$has_input_connection()}\if{html}{\out{
}}
}
+}
\if{html}{\out{
}}
\if{html}{\out{}}
\if{latex}{\out{\hypertarget{method-process-has_output_connection}{}}}
-\subsection{\code{process$has_output_connection()}}{
- \verb{$has_output_connection()} returns \code{TRUE} if there is a connection
+\subsection{Method \code{has_output_connection()}}{
+\verb{$has_output_connection()} returns \code{TRUE} if there is a connection
object for standard output; in other words, if \code{stdout="|"}. It returns
\code{FALSE} otherwise.
- \subsection{Usage}{
- \if{html}{\out{}}
- \preformatted{process$has_output_connection()}
- \if{html}{\out{
}}
- }
+\subsection{Usage}{
+\if{html}{\out{}}\preformatted{process$has_output_connection()}\if{html}{\out{
}}
}
+}
\if{html}{\out{
}}
\if{html}{\out{}}
\if{latex}{\out{\hypertarget{method-process-has_error_connection}{}}}
-\subsection{\code{process$has_error_connection()}}{
- \verb{$has_error_connection()} returns \code{TRUE} if there is a connection
+\subsection{Method \code{has_error_connection()}}{
+\verb{$has_error_connection()} returns \code{TRUE} if there is a connection
object for standard error; in other words, if \code{stderr="|"}. It returns
\code{FALSE} otherwise.
- \subsection{Usage}{
- \if{html}{\out{}}
- \preformatted{process$has_error_connection()}
- \if{html}{\out{
}}
- }
+\subsection{Usage}{
+\if{html}{\out{}}\preformatted{process$has_error_connection()}\if{html}{\out{
}}
}
+}
\if{html}{\out{
}}
\if{html}{\out{}}
\if{latex}{\out{\hypertarget{method-process-has_poll_connection}{}}}
-\subsection{\code{process$has_poll_connection()}}{
- \verb{$has_poll_connection()} return \code{TRUE} if there is a poll connection,
+\subsection{Method \code{has_poll_connection()}}{
+\verb{$has_poll_connection()} return \code{TRUE} if there is a poll connection,
\code{FALSE} otherwise.
- \subsection{Usage}{
- \if{html}{\out{}}
- \preformatted{process$has_poll_connection()}
- \if{html}{\out{
}}
- }
+\subsection{Usage}{
+\if{html}{\out{}}\preformatted{process$has_poll_connection()}\if{html}{\out{
}}
}
+}
\if{html}{\out{
}}
\if{html}{\out{}}
\if{latex}{\out{\hypertarget{method-process-get_input_connection}{}}}
-\subsection{\code{process$get_input_connection()}}{
- \verb{$get_input_connection()} returns a connection object, to the
+\subsection{Method \code{get_input_connection()}}{
+\verb{$get_input_connection()} returns a connection object, to the
standard input stream of the process.
- \subsection{Usage}{
- \if{html}{\out{}}
- \preformatted{process$get_input_connection()}
- \if{html}{\out{
}}
- }
+\subsection{Usage}{
+\if{html}{\out{}}\preformatted{process$get_input_connection()}\if{html}{\out{
}}
}
+}
\if{html}{\out{
}}
\if{html}{\out{}}
\if{latex}{\out{\hypertarget{method-process-get_output_connection}{}}}
-\subsection{\code{process$get_output_connection()}}{
- \verb{$get_output_connection()} returns a connection object, to the
+\subsection{Method \code{get_output_connection()}}{
+\verb{$get_output_connection()} returns a connection object, to the
standard output stream of the process.
- \subsection{Usage}{
- \if{html}{\out{}}
- \preformatted{process$get_output_connection()}
- \if{html}{\out{
}}
- }
+\subsection{Usage}{
+\if{html}{\out{}}\preformatted{process$get_output_connection()}\if{html}{\out{
}}
}
+}
\if{html}{\out{
}}
\if{html}{\out{}}
\if{latex}{\out{\hypertarget{method-process-get_error_connection}{}}}
-\subsection{\code{process$get_error_connection()}}{
- \verb{$get_error_conneciton()} returns a connection object, to the
+\subsection{Method \code{get_error_connection()}}{
+\verb{$get_error_conneciton()} returns a connection object, to the
standard error stream of the process.
- \subsection{Usage}{
- \if{html}{\out{}}
- \preformatted{process$get_error_connection()}
- \if{html}{\out{
}}
- }
+\subsection{Usage}{
+\if{html}{\out{}}\preformatted{process$get_error_connection()}\if{html}{\out{
}}
}
+}
\if{html}{\out{
}}
\if{html}{\out{}}
\if{latex}{\out{\hypertarget{method-process-read_all_output}{}}}
-\subsection{\code{process$read_all_output()}}{
- \verb{$read_all_output()} waits for all standard output from the process.
+\subsection{Method \code{read_all_output()}}{
+\verb{$read_all_output()} waits for all standard output from the process.
It does not return until the process has finished.
Note that this process involves waiting for the process to finish,
polling for I/O and potentially several \code{readLines()} calls.
It returns a character scalar. This will return content only if
\code{stdout="|"} was used. Otherwise, it will throw an error.
- \subsection{Usage}{
- \if{html}{\out{}}
- \preformatted{process$read_all_output()}
- \if{html}{\out{
}}
- }
+\subsection{Usage}{
+\if{html}{\out{}}\preformatted{process$read_all_output()}\if{html}{\out{
}}
}
+}
\if{html}{\out{
}}
\if{html}{\out{}}
\if{latex}{\out{\hypertarget{method-process-read_all_error}{}}}
-\subsection{\code{process$read_all_error()}}{
- \verb{$read_all_error()} waits for all standard error from the process.
+\subsection{Method \code{read_all_error()}}{
+\verb{$read_all_error()} waits for all standard error from the process.
It does not return until the process has finished.
Note that this process involves waiting for the process to finish,
polling for I/O and potentially several \code{readLines()} calls.
It returns a character scalar. This will return content only if
\code{stderr="|"} was used. Otherwise, it will throw an error.
- \subsection{Usage}{
- \if{html}{\out{}}
- \preformatted{process$read_all_error()}
- \if{html}{\out{
}}
- }
+\subsection{Usage}{
+\if{html}{\out{}}\preformatted{process$read_all_error()}\if{html}{\out{
}}
}
+}
\if{html}{\out{
}}
\if{html}{\out{}}
\if{latex}{\out{\hypertarget{method-process-read_all_output_lines}{}}}
-\subsection{\code{process$read_all_output_lines()}}{
- \verb{$read_all_output_lines()} waits for all standard output lines
+\subsection{Method \code{read_all_output_lines()}}{
+\verb{$read_all_output_lines()} waits for all standard output lines
from a process. It does not return until the process has finished.
Note that this process involves waiting for the process to finish,
polling for I/O and potentially several \code{readLines()} calls.
It returns a character vector. This will return content only if
\code{stdout="|"} was used. Otherwise, it will throw an error.
- \subsection{Usage}{
- \if{html}{\out{}}
- \preformatted{process$read_all_output_lines()}
- \if{html}{\out{
}}
- }
+\subsection{Usage}{
+\if{html}{\out{}}\preformatted{process$read_all_output_lines()}\if{html}{\out{
}}
}
+}
\if{html}{\out{
}}
\if{html}{\out{}}
\if{latex}{\out{\hypertarget{method-process-read_all_error_lines}{}}}
-\subsection{\code{process$read_all_error_lines()}}{
- \verb{$read_all_error_lines()} waits for all standard error lines from
+\subsection{Method \code{read_all_error_lines()}}{
+\verb{$read_all_error_lines()} waits for all standard error lines from
a process. It does not return until the process has finished.
Note that this process involves waiting for the process to finish,
polling for I/O and potentially several \code{readLines()} calls.
It returns a character vector. This will return content only if
\code{stderr="|"} was used. Otherwise, it will throw an error.
- \subsection{Usage}{
- \if{html}{\out{}}
- \preformatted{process$read_all_error_lines()}
- \if{html}{\out{
}}
- }
+\subsection{Usage}{
+\if{html}{\out{}}\preformatted{process$read_all_error_lines()}\if{html}{\out{
}}
}
+}
\if{html}{\out{
}}
\if{html}{\out{}}
\if{latex}{\out{\hypertarget{method-process-write_input}{}}}
-\subsection{\code{process$write_input()}}{
- \verb{$write_input()} writes the character vector (separated by \code{sep}) to
+\subsection{Method \code{write_input()}}{
+\verb{$write_input()} writes the character vector (separated by \code{sep}) to
the standard input of the process. It will be converted to the specified
encoding. This operation is non-blocking, and it will return, even if
the write fails (because the write buffer is full), or if it suceeds
@@ -976,268 +930,231 @@ partially (i.e. not the full string is written). It returns with a raw
vector, that contains the bytes that were not written. You can supply
this raw vector to \verb{$write_input()} again, until it is fully written,
and then the return value will be \code{raw(0)} (invisibly).
- \subsection{Usage}{
- \if{html}{\out{}}
- \preformatted{process$write_input(str, sep = "\\n")}
- \if{html}{\out{
}}
- }
- \subsection{Arguments}{
- \if{html}{\out{}}
- \describe{
- \item{\code{str}}{Character or raw vector to write to the standard input
+\subsection{Usage}{
+\if{html}{\out{
}}\preformatted{process$write_input(str, sep = "\\n")}\if{html}{\out{
}}
+}
+
+\subsection{Arguments}{
+\if{html}{\out{
}}
+\describe{
+\item{\code{str}}{Character or raw vector to write to the standard input
of the process. If a character vector with a marked encoding,
it will be converted to \code{encoding}.}
- \item{\code{sep}}{Separator to add between \code{str} elements if it is a
+
+\item{\code{sep}}{Separator to add between \code{str} elements if it is a
character vector. It is ignored if \code{str} is a raw vector.}
- }
- \if{html}{\out{
}}
- }
- \subsection{Returns}{
- Leftover text (as a raw vector), that was not written.
- }
}
-
+\if{html}{\out{
}}
+}
+\subsection{Returns}{
+Leftover text (as a raw vector), that was not written.
+}
+}
\if{html}{\out{
}}
\if{html}{\out{}}
\if{latex}{\out{\hypertarget{method-process-get_input_file}{}}}
-\subsection{\code{process$get_input_file()}}{
- \verb{$get_input_file()} if the \code{stdin} argument was a filename,
+\subsection{Method \code{get_input_file()}}{
+\verb{$get_input_file()} if the \code{stdin} argument was a filename,
this returns the absolute path to the file. If \code{stdin} was \code{"|"} or
\code{NULL}, this simply returns that value.
- \subsection{Usage}{
- \if{html}{\out{}}
- \preformatted{process$get_input_file()}
- \if{html}{\out{
}}
- }
+\subsection{Usage}{
+\if{html}{\out{}}\preformatted{process$get_input_file()}\if{html}{\out{
}}
}
+}
\if{html}{\out{
}}
\if{html}{\out{}}
\if{latex}{\out{\hypertarget{method-process-get_output_file}{}}}
-\subsection{\code{process$get_output_file()}}{
- \verb{$get_output_file()} if the \code{stdout} argument was a filename,
+\subsection{Method \code{get_output_file()}}{
+\verb{$get_output_file()} if the \code{stdout} argument was a filename,
this returns the absolute path to the file. If \code{stdout} was \code{"|"} or
\code{NULL}, this simply returns that value.
- \subsection{Usage}{
- \if{html}{\out{}}
- \preformatted{process$get_output_file()}
- \if{html}{\out{
}}
- }
+\subsection{Usage}{
+\if{html}{\out{}}\preformatted{process$get_output_file()}\if{html}{\out{
}}
}
+}
\if{html}{\out{
}}
\if{html}{\out{}}
\if{latex}{\out{\hypertarget{method-process-get_error_file}{}}}
-\subsection{\code{process$get_error_file()}}{
- \verb{$get_error_file()} if the \code{stderr} argument was a filename,
+\subsection{Method \code{get_error_file()}}{
+\verb{$get_error_file()} if the \code{stderr} argument was a filename,
this returns the absolute path to the file. If \code{stderr} was \code{"|"} or
\code{NULL}, this simply returns that value.
- \subsection{Usage}{
- \if{html}{\out{}}
- \preformatted{process$get_error_file()}
- \if{html}{\out{
}}
- }
+\subsection{Usage}{
+\if{html}{\out{}}\preformatted{process$get_error_file()}\if{html}{\out{
}}
}
+}
\if{html}{\out{
}}
\if{html}{\out{}}
\if{latex}{\out{\hypertarget{method-process-poll_io}{}}}
-\subsection{\code{process$poll_io()}}{
- \verb{$poll_io()} polls the process's connections for I/O. See more in
+\subsection{Method \code{poll_io()}}{
+\verb{$poll_io()} polls the process's connections for I/O. See more in
the \emph{Polling} section, and see also the \code{\link[=poll]{poll()}} function
to poll on multiple processes.
- \subsection{Usage}{
- \if{html}{\out{}}
- \preformatted{process$poll_io(timeout)}
- \if{html}{\out{
}}
- }
- \subsection{Arguments}{
- \if{html}{\out{}}
- \describe{
- \item{\code{timeout}}{Timeout in milliseconds, for the wait or the I/O
-polling.}
- }
- \if{html}{\out{
}}
- }
+\subsection{Usage}{
+\if{html}{\out{}}\preformatted{process$poll_io(timeout)}\if{html}{\out{
}}
}
+\subsection{Arguments}{
+\if{html}{\out{}}
+\describe{
+\item{\code{timeout}}{Timeout in milliseconds, for the wait or the I/O
+polling.}
+}
+\if{html}{\out{
}}
+}
+}
\if{html}{\out{
}}
\if{html}{\out{}}
\if{latex}{\out{\hypertarget{method-process-get_poll_connection}{}}}
-\subsection{\code{process$get_poll_connection()}}{
- \verb{$get_poll_connetion()} returns the poll connection, if the process has
+\subsection{Method \code{get_poll_connection()}}{
+\verb{$get_poll_connetion()} returns the poll connection, if the process has
one.
- \subsection{Usage}{
- \if{html}{\out{}}
- \preformatted{process$get_poll_connection()}
- \if{html}{\out{
}}
- }
+\subsection{Usage}{
+\if{html}{\out{}}\preformatted{process$get_poll_connection()}\if{html}{\out{
}}
}
+}
\if{html}{\out{
}}
\if{html}{\out{}}
\if{latex}{\out{\hypertarget{method-process-get_result}{}}}
-\subsection{\code{process$get_result()}}{
- \verb{$get_result()} returns the result of the post processesing function.
+\subsection{Method \code{get_result()}}{
+\verb{$get_result()} returns the result of the post processesing function.
It can only be called once the process has finished. If the process has
no post-processing function, then \code{NULL} is returned.
- \subsection{Usage}{
- \if{html}{\out{}}
- \preformatted{process$get_result()}
- \if{html}{\out{
}}
- }
+\subsection{Usage}{
+\if{html}{\out{}}\preformatted{process$get_result()}\if{html}{\out{
}}
}
+}
\if{html}{\out{
}}
\if{html}{\out{}}
\if{latex}{\out{\hypertarget{method-process-as_ps_handle}{}}}
-\subsection{\code{process$as_ps_handle()}}{
- \verb{$as_ps_handle()} returns a \link[ps:ps_handle]{ps::ps_handle} object, corresponding to
+\subsection{Method \code{as_ps_handle()}}{
+\verb{$as_ps_handle()} returns a \link[ps:ps_handle]{ps::ps_handle} object, corresponding to
the process.
- \subsection{Usage}{
- \if{html}{\out{}}
- \preformatted{process$as_ps_handle()}
- \if{html}{\out{
}}
- }
+\subsection{Usage}{
+\if{html}{\out{}}\preformatted{process$as_ps_handle()}\if{html}{\out{
}}
}
+}
\if{html}{\out{
}}
\if{html}{\out{}}
\if{latex}{\out{\hypertarget{method-process-get_name}{}}}
-\subsection{\code{process$get_name()}}{
- Calls \code{\link[ps:ps_name]{ps::ps_name()}} to get the process name.
- \subsection{Usage}{
- \if{html}{\out{}}
- \preformatted{process$get_name()}
- \if{html}{\out{
}}
- }
+\subsection{Method \code{get_name()}}{
+Calls \code{\link[ps:ps_name]{ps::ps_name()}} to get the process name.
+\subsection{Usage}{
+\if{html}{\out{}}\preformatted{process$get_name()}\if{html}{\out{
}}
}
+}
\if{html}{\out{
}}
\if{html}{\out{}}
\if{latex}{\out{\hypertarget{method-process-get_exe}{}}}
-\subsection{\code{process$get_exe()}}{
- Calls \code{\link[ps:ps_exe]{ps::ps_exe()}} to get the path of the executable.
- \subsection{Usage}{
- \if{html}{\out{}}
- \preformatted{process$get_exe()}
- \if{html}{\out{
}}
- }
+\subsection{Method \code{get_exe()}}{
+Calls \code{\link[ps:ps_exe]{ps::ps_exe()}} to get the path of the executable.
+\subsection{Usage}{
+\if{html}{\out{}}\preformatted{process$get_exe()}\if{html}{\out{
}}
}
+}
\if{html}{\out{
}}
\if{html}{\out{}}
\if{latex}{\out{\hypertarget{method-process-get_cmdline}{}}}
-\subsection{\code{process$get_cmdline()}}{
- Calls \code{\link[ps:ps_cmdline]{ps::ps_cmdline()}} to get the command line.
- \subsection{Usage}{
- \if{html}{\out{}}
- \preformatted{process$get_cmdline()}
- \if{html}{\out{
}}
- }
+\subsection{Method \code{get_cmdline()}}{
+Calls \code{\link[ps:ps_cmdline]{ps::ps_cmdline()}} to get the command line.
+\subsection{Usage}{
+\if{html}{\out{}}\preformatted{process$get_cmdline()}\if{html}{\out{
}}
}
+}
\if{html}{\out{
}}
\if{html}{\out{}}
\if{latex}{\out{\hypertarget{method-process-get_status}{}}}
-\subsection{\code{process$get_status()}}{
- Calls \code{\link[ps:ps_status]{ps::ps_status()}} to get the process status.
- \subsection{Usage}{
- \if{html}{\out{}}
- \preformatted{process$get_status()}
- \if{html}{\out{
}}
- }
+\subsection{Method \code{get_status()}}{
+Calls \code{\link[ps:ps_status]{ps::ps_status()}} to get the process status.
+\subsection{Usage}{
+\if{html}{\out{}}\preformatted{process$get_status()}\if{html}{\out{
}}
}
+}
\if{html}{\out{
}}
\if{html}{\out{}}
\if{latex}{\out{\hypertarget{method-process-get_username}{}}}
-\subsection{\code{process$get_username()}}{
- calls \code{\link[ps:ps_username]{ps::ps_username()}} to get the username.
- \subsection{Usage}{
- \if{html}{\out{}}
- \preformatted{process$get_username()}
- \if{html}{\out{
}}
- }
+\subsection{Method \code{get_username()}}{
+calls \code{\link[ps:ps_username]{ps::ps_username()}} to get the username.
+\subsection{Usage}{
+\if{html}{\out{}}\preformatted{process$get_username()}\if{html}{\out{
}}
}
+}
\if{html}{\out{
}}
\if{html}{\out{}}
\if{latex}{\out{\hypertarget{method-process-get_wd}{}}}
-\subsection{\code{process$get_wd()}}{
- Calls \code{\link[ps:ps_cwd]{ps::ps_cwd()}} to get the current working directory.
- \subsection{Usage}{
- \if{html}{\out{}}
- \preformatted{process$get_wd()}
- \if{html}{\out{
}}
- }
+\subsection{Method \code{get_wd()}}{
+Calls \code{\link[ps:ps_cwd]{ps::ps_cwd()}} to get the current working directory.
+\subsection{Usage}{
+\if{html}{\out{}}\preformatted{process$get_wd()}\if{html}{\out{
}}
}
+}
\if{html}{\out{
}}
\if{html}{\out{}}
\if{latex}{\out{\hypertarget{method-process-get_cpu_times}{}}}
-\subsection{\code{process$get_cpu_times()}}{
- Calls \code{\link[ps:ps_cpu_times]{ps::ps_cpu_times()}} to get CPU usage data.
- \subsection{Usage}{
- \if{html}{\out{}}
- \preformatted{process$get_cpu_times()}
- \if{html}{\out{
}}
- }
+\subsection{Method \code{get_cpu_times()}}{
+Calls \code{\link[ps:ps_cpu_times]{ps::ps_cpu_times()}} to get CPU usage data.
+\subsection{Usage}{
+\if{html}{\out{}}\preformatted{process$get_cpu_times()}\if{html}{\out{
}}
}
+}
\if{html}{\out{
}}
\if{html}{\out{}}
\if{latex}{\out{\hypertarget{method-process-get_memory_info}{}}}
-\subsection{\code{process$get_memory_info()}}{
- Calls \code{\link[ps:ps_memory_info]{ps::ps_memory_info()}} to get memory data.
- \subsection{Usage}{
- \if{html}{\out{}}
- \preformatted{process$get_memory_info()}
- \if{html}{\out{
}}
- }
+\subsection{Method \code{get_memory_info()}}{
+Calls \code{\link[ps:ps_memory_info]{ps::ps_memory_info()}} to get memory data.
+\subsection{Usage}{
+\if{html}{\out{}}\preformatted{process$get_memory_info()}\if{html}{\out{
}}
}
+}
\if{html}{\out{
}}
\if{html}{\out{}}
\if{latex}{\out{\hypertarget{method-process-suspend}{}}}
-\subsection{\code{process$suspend()}}{
- Calls \code{\link[ps:ps_suspend]{ps::ps_suspend()}} to suspend the process.
- \subsection{Usage}{
- \if{html}{\out{}}
- \preformatted{process$suspend()}
- \if{html}{\out{
}}
- }
+\subsection{Method \code{suspend()}}{
+Calls \code{\link[ps:ps_suspend]{ps::ps_suspend()}} to suspend the process.
+\subsection{Usage}{
+\if{html}{\out{}}\preformatted{process$suspend()}\if{html}{\out{
}}
}
+}
\if{html}{\out{
}}
\if{html}{\out{}}
\if{latex}{\out{\hypertarget{method-process-resume}{}}}
-\subsection{\code{process$resume()}}{
- Calls \code{\link[ps:ps_resume]{ps::ps_resume()}} to resume a suspended process.
- \subsection{Usage}{
- \if{html}{\out{}}
- \preformatted{process$resume()}
- \if{html}{\out{
}}
- }
+\subsection{Method \code{resume()}}{
+Calls \code{\link[ps:ps_resume]{ps::ps_resume()}} to resume a suspended process.
+\subsection{Usage}{
+\if{html}{\out{}}\preformatted{process$resume()}\if{html}{\out{
}}
}
+}
\if{html}{\out{
}}
\if{html}{\out{}}
\if{latex}{\out{\hypertarget{method-process-clone}{}}}
-\subsection{\code{process$clone()}}{
- The objects of this class are cloneable with this method.
- \subsection{Usage}{
- \if{html}{\out{}}
- \preformatted{process$clone(deep = FALSE)}
- \if{html}{\out{
}}
- }
- \subsection{Arguments}{
- \if{html}{\out{}}
- \describe{
- \item{\code{deep}}{Whether to make a deep clone.}
- }
- \if{html}{\out{
}}
- }
+\subsection{Method \code{clone()}}{
+The objects of this class are cloneable with this method.
+\subsection{Usage}{
+\if{html}{\out{}}\preformatted{process$clone(deep = FALSE)}\if{html}{\out{
}}
}
+\subsection{Arguments}{
+\if{html}{\out{}}
+\describe{
+\item{\code{deep}}{Whether to make a deep clone.}
+}
+\if{html}{\out{
}}
+}
+}
}
diff --git a/man/processx-package.Rd b/man/processx-package.Rd
index 48a2c775..f672b9f3 100644
--- a/man/processx-package.Rd
+++ b/man/processx-package.Rd
@@ -22,7 +22,6 @@ Useful links:
Authors:
\itemize{
- \item Gábor Csárdi \email{csardi.gabor@gmail.com} (\href{https://orcid.org/0000-0001-7098-9676}{ORCID}) [copyright holder]
\item Winston Chang
}
diff --git a/man/processx_connections.Rd b/man/processx_connections.Rd
index 0337f1fa..9dc96a70 100644
--- a/man/processx_connections.Rd
+++ b/man/processx_connections.Rd
@@ -4,6 +4,7 @@
\alias{conn_create_fd}
\alias{conn_file_name}
\alias{conn_create_pipepair}
+\alias{conn_create_proc_pipepair}
\alias{conn_read_chars}
\alias{conn_read_chars.processx_connection}
\alias{processx_conn_read_chars}
@@ -35,6 +36,8 @@ conn_file_name(con)
conn_create_pipepair(encoding = "", nonblocking = c(TRUE, FALSE))
+conn_create_proc_pipepair(encoding = "")
+
conn_read_chars(con, n = -1)
\method{conn_read_chars}{processx_connection}(con, n = -1)
@@ -105,9 +108,9 @@ characters or lines.}
\item{sep}{Separator to use if \code{str} is a character vector. Ignored if
\code{str} is a raw vector.}
-\item{filename}{File name. For \code{conn_create_pipe()} on Windows, a
+\item{filename}{File name. For \code{conn_create_fifo()} on Windows, a
\verb{\\\\?\\pipe} prefix is added to this, if it does not have such a prefix.
-For \code{conn_create_pipe()} it can also be \code{NULL}, in which case a random
+For \code{conn_create_fifo()} it can also be \code{NULL}, in which case a random
file name is used via \code{tempfile()}.}
\item{read}{Whether the connection is readable.}
@@ -136,6 +139,13 @@ where it returns the full name of the pipe.
\code{conn_create_pipepair()} creates a pair of connected connections, the
first one is writeable, the second one is readable.
+\code{conn_create_proc_pipepair()} creates a unidirectional pipe suitable for
+connecting two child processes: the first element is the write end (pass as
+\code{stdout} to the writing process) and the second is the read end (pass as
+\code{stdin} to the reading process). Unlike \code{conn_create_pipepair()}, both ends
+are synchronous (blocking), which is required for child-process stdin/stdout
+on Windows.
+
\code{conn_read_chars()} reads UTF-8 characters from the connections. If the
connection itself is not UTF-8 encoded, it re-encodes it.
diff --git a/man/processx_fifos.Rd b/man/processx_fifos.Rd
index 71586b60..1bc52777 100644
--- a/man/processx_fifos.Rd
+++ b/man/processx_fifos.Rd
@@ -25,7 +25,7 @@ conn_connect_fifo(
\item{filename}{File name of the FIFO. On Windows it the name of the
pipe within the \verb{\\\\?\\pipe\\} namespace, either the full name, or the
part after that prefix. If \code{NULL}, then a random name
-is used, on Unix in the R temporary directory: \code{\link[base:tempdir]{base::tempdir()}}.}
+is used, on Unix in the R temporary directory: \code{\link[base:tempfile]{base::tempdir()}}.}
\item{read}{If \code{TRUE} then connect to the read end of the FIFO.
Exactly one of \code{read} and \code{write} must be set to \code{TRUE}.}
diff --git a/man/processx_sockets.Rd b/man/processx_sockets.Rd
index f4c33add..a606da42 100644
--- a/man/processx_sockets.Rd
+++ b/man/processx_sockets.Rd
@@ -19,7 +19,7 @@ conn_unix_socket_state(con)
\item{filename}{File name of the socket. On Windows it the name of the
pipe within the \verb{\\\\?\\pipe\\} namespace, either the full name, or the
part after that prefix. If \code{NULL}, then a random name
-is used, on Unix in the R temporary directory: \code{\link[base:tempdir]{base::tempdir()}}.}
+is used, on Unix in the R temporary directory: \code{\link[base:tempfile]{base::tempdir()}}.}
\item{encoding}{Encoding to assume when reading from the socket.}
diff --git a/src/init.c b/src/init.c
index a4b5f571..b1ae202d 100644
--- a/src/init.c
+++ b/src/init.c
@@ -142,6 +142,8 @@ static const R_CallMethodDef callMethods[] = {
(DL_FUNC) processx_connection_socket_state, 1 },
{ "processx_connection_create_pipepair",
(DL_FUNC) processx_connection_create_pipepair, 2 },
+ { "processx_connection_create_proc_pipepair",
+ (DL_FUNC) processx_connection_create_proc_pipepair, 1 },
{ "processx_connection_create_fd", (DL_FUNC) &processx_connection_create_fd, 3 },
{ "processx_connection_create_file",
(DL_FUNC) &processx_connection_create_file, 3 },
diff --git a/src/processx-connection.c b/src/processx-connection.c
index 01fccace..d6c6ddbc 100644
--- a/src/processx-connection.c
+++ b/src/processx-connection.c
@@ -660,6 +660,55 @@ SEXP processx_connection_create_pipepair(SEXP encoding, SEXP nonblocking) {
return result;
}
+SEXP processx_connection_create_proc_pipepair(SEXP encoding) {
+ const char *c_encoding = CHAR(STRING_ELT(encoding, 0));
+ SEXP result, con1, con2;
+
+#ifdef _WIN32
+ HANDLE hRead = INVALID_HANDLE_VALUE;
+ HANDLE hWrite = INVALID_HANDLE_VALUE;
+ SECURITY_ATTRIBUTES sa;
+ sa.nLength = sizeof(sa);
+ sa.lpSecurityDescriptor = NULL;
+ sa.bInheritHandle = TRUE;
+
+ if (!CreatePipe(&hRead, &hWrite, &sa, 0)) {
+ R_THROW_SYSTEM_ERROR("Cannot create pipe");
+ }
+
+ /* con1 = write end [[1]], con2 = read end [[2]] */
+ processx_c_connection_create(hWrite, PROCESSX_FILE_TYPE_PIPE,
+ c_encoding, NULL, &con1);
+ PROTECT(con1);
+ processx_c_connection_create(hRead, PROCESSX_FILE_TYPE_PIPE,
+ c_encoding, NULL, &con2);
+ PROTECT(con2);
+
+#else
+ int fds[2];
+ if (pipe(fds) == -1) {
+ R_THROW_SYSTEM_ERROR("Cannot create pipe");
+ }
+ processx__cloexec_fcntl(fds[0], 1);
+ processx__cloexec_fcntl(fds[1], 1);
+
+ /* fds[1] = write end [[1]], fds[0] = read end [[2]], both blocking */
+ processx_c_connection_create(fds[1], PROCESSX_FILE_TYPE_PIPE,
+ c_encoding, NULL, &con1);
+ PROTECT(con1);
+ processx_c_connection_create(fds[0], PROCESSX_FILE_TYPE_PIPE,
+ c_encoding, NULL, &con2);
+ PROTECT(con2);
+#endif
+
+ result = PROTECT(allocVector(VECSXP, 2));
+ SET_VECTOR_ELT(result, 0, con1); /* [[1]] = write end */
+ SET_VECTOR_ELT(result, 1, con2); /* [[2]] = read end */
+
+ UNPROTECT(3);
+ return result;
+}
+
SEXP processx__connection_set_std(SEXP con, int which, int drop) {
processx_connection_t *ccon = R_ExternalPtrAddr(con);
if (!ccon) R_THROW_ERROR("Invalid connection object");
diff --git a/src/processx-connection.h b/src/processx-connection.h
index aed4d48e..40a004ed 100644
--- a/src/processx-connection.h
+++ b/src/processx-connection.h
@@ -184,6 +184,7 @@ SEXP processx_connection_poll(SEXP pollables, SEXP timeout);
/* Functions for connection inheritance */
SEXP processx_connection_create_pipepair(SEXP encoding, SEXP nonblocking);
+SEXP processx_connection_create_proc_pipepair(SEXP encoding);
SEXP processx_connection_set_stdout(SEXP con, SEXP drop);
diff --git a/src/tools/px.c b/src/tools/px.c
index 89b1f6e3..46f71365 100644
--- a/src/tools/px.c
+++ b/src/tools/px.c
@@ -35,7 +35,7 @@ void usage(void) {
fprintf(stderr, " errflush -- "
"flush stderr stream\n");
fprintf(stderr, " cat -- "
- "print file to stdout\n");
+ "print file to stdout (use '' for standard input)\n");
fprintf(stderr, " return -- "
"return with exitcode\n");
fprintf(stderr, " writefile -- "
diff --git a/tests/testthat/_snaps/Darwin/unix-sockets.md b/tests/testthat/_snaps/Darwin/unix-sockets.md
index a923fcc0..30dd74c6 100644
--- a/tests/testthat/_snaps/Darwin/unix-sockets.md
+++ b/tests/testthat/_snaps/Darwin/unix-sockets.md
@@ -5,8 +5,8 @@
Condition
Error in `processx_conn_read_chars()`:
! ! Native call to `processx_connection_read_chars` failed
- Caused by error in `chain_call(c_processx_connection_read_chars, con, n)` at connections.R:302::
- ! Cannot read from processx connection (system error 57, Socket is not connected) @processx-connection.c:1891 (processx__connection_read)
+ Caused by error in `chain_call(c_processx_connection_read_chars, con, n)` at connections.R:318::
+ ! Cannot read from processx connection (system error 57, Socket is not connected) @processx-connection.c:1940 (processx__connection_read)
# errors
@@ -15,20 +15,20 @@
Condition
Error in `conn_create_unix_socket()`:
! ! Native call to `processx_connection_create_socket` failed
- Caused by error in `chain_call(c_processx_connection_create_socket, filename, encoding)` at connections.R:618::
+ Caused by error in `chain_call(c_processx_connection_create_socket, filename, encoding)` at connections.R:634::
! Server socket path too long: /
Code
conn_create_unix_socket("/dev/null")
Condition
Error in `conn_create_unix_socket()`:
! ! Native call to `processx_connection_create_socket` failed
- Caused by error in `chain_call(c_processx_connection_create_socket, filename, encoding)` at connections.R:618::
+ Caused by error in `chain_call(c_processx_connection_create_socket, filename, encoding)` at connections.R:634::
! Cannot bind to socket (system error 48, Address already in use) @processx-connection.c:479 (processx_connection_create_socket)
Code
conn_connect_unix_socket("/dev/null")
Condition
Error in `conn_connect_unix_socket()`:
! ! Native call to `processx_connection_connect_socket` failed
- Caused by error in `chain_call(c_processx_connection_connect_socket, filename, encoding)` at connections.R:640::
+ Caused by error in `chain_call(c_processx_connection_connect_socket, filename, encoding)` at connections.R:656::
! Cannot connect to socket (system error 38, Socket operation on non-socket) @processx-connection.c:550 (processx_connection_connect_socket)
diff --git a/tests/testthat/_snaps/Linux/unix-sockets.md b/tests/testthat/_snaps/Linux/unix-sockets.md
index 4312c943..9fc076f0 100644
--- a/tests/testthat/_snaps/Linux/unix-sockets.md
+++ b/tests/testthat/_snaps/Linux/unix-sockets.md
@@ -5,8 +5,8 @@
Condition
Error in `processx_conn_read_chars()`:
! ! Native call to `processx_connection_read_chars` failed
- Caused by error in `chain_call(c_processx_connection_read_chars, con, n)` at connections.R:302::
- ! Cannot read from processx connection (system error 22, Invalid argument) @processx-connection.c:1891 (processx__connection_read)
+ Caused by error in `chain_call(c_processx_connection_read_chars, con, n)` at connections.R:318::
+ ! Cannot read from processx connection (system error 22, Invalid argument) @processx-connection.c:1940 (processx__connection_read)
# errors
@@ -15,20 +15,20 @@
Condition
Error in `conn_create_unix_socket()`:
! ! Native call to `processx_connection_create_socket` failed
- Caused by error in `chain_call(c_processx_connection_create_socket, filename, encoding)` at connections.R:618::
+ Caused by error in `chain_call(c_processx_connection_create_socket, filename, encoding)` at connections.R:634::
! Server socket path too long: /
Code
conn_create_unix_socket("/dev/null")
Condition
Error in `conn_create_unix_socket()`:
! ! Native call to `processx_connection_create_socket` failed
- Caused by error in `chain_call(c_processx_connection_create_socket, filename, encoding)` at connections.R:618::
+ Caused by error in `chain_call(c_processx_connection_create_socket, filename, encoding)` at connections.R:634::
! Cannot bind to socket (system error 98, Address already in use) @processx-connection.c:479 (processx_connection_create_socket)
Code
conn_connect_unix_socket("/dev/null")
Condition
Error in `conn_connect_unix_socket()`:
! ! Native call to `processx_connection_connect_socket` failed
- Caused by error in `chain_call(c_processx_connection_connect_socket, filename, encoding)` at connections.R:640::
+ Caused by error in `chain_call(c_processx_connection_connect_socket, filename, encoding)` at connections.R:656::
! Cannot connect to socket (system error 111, Connection refused) @processx-connection.c:550 (processx_connection_connect_socket)
diff --git a/tests/testthat/_snaps/Windows/unix-sockets.md b/tests/testthat/_snaps/Windows/unix-sockets.md
index 1315e639..d0ce4761 100644
--- a/tests/testthat/_snaps/Windows/unix-sockets.md
+++ b/tests/testthat/_snaps/Windows/unix-sockets.md
@@ -5,6 +5,6 @@
Condition
Error in `processx_conn_read_chars()`:
! ! Native call to `processx_connection_read_chars` failed
- Caused by error in `chain_call(c_processx_connection_read_chars, con, n)` at connections.R:302::
- ! Cannot read from an un-accepted socket connection @processx-connection.c:1780 (processx__connection_read)
+ Caused by error in `chain_call(c_processx_connection_read_chars, con, n)` at connections.R:318::
+ ! Cannot read from an un-accepted socket connection @processx-connection.c:1829 (processx__connection_read)
diff --git a/tests/testthat/_snaps/unix-sockets.md b/tests/testthat/_snaps/unix-sockets.md
index 5fd6d1bb..ac90e7ed 100644
--- a/tests/testthat/_snaps/unix-sockets.md
+++ b/tests/testthat/_snaps/unix-sockets.md
@@ -5,7 +5,7 @@
Condition
Error in `conn_accept_unix_socket()`:
! ! Native call to `processx_connection_accept_socket` failed
- Caused by error in `chain_call(c_processx_connection_accept_socket, con)` at connections.R:653::
+ Caused by error in `chain_call(c_processx_connection_accept_socket, con)` at connections.R:669::
! Socket is not listening @processx-connection.c:577 (processx_connection_accept_socket)
# writing unaccepted server socket is error
@@ -15,8 +15,8 @@
Condition
Error in `processx_conn_write()`:
! ! Native call to `processx_connection_write_bytes` failed
- Caused by error in `chain_call(c_processx_connection_write_bytes, con, str)` at connections.R:424::
- ! Cannot write to an un-accepted socket connection @processx-connection.c:1009 (processx_c_connection_write_bytes)
+ Caused by error in `chain_call(c_processx_connection_write_bytes, con, str)` at connections.R:440::
+ ! Cannot write to an un-accepted socket connection @processx-connection.c:1058 (processx_c_connection_write_bytes)
# errors
@@ -25,7 +25,7 @@
Condition
Error in `conn_accept_unix_socket()`:
! ! Native call to `processx_connection_accept_socket` failed
- Caused by error in `chain_call(c_processx_connection_accept_socket, con)` at connections.R:653::
+ Caused by error in `chain_call(c_processx_connection_accept_socket, con)` at connections.R:669::
! Not a socket connection @processx-connection.c:573 (processx_connection_accept_socket)
---
@@ -35,6 +35,6 @@
Condition
Error in `conn_unix_socket_state()`:
! ! Native call to `processx_connection_socket_state` failed
- Caused by error in `chain_call(c_processx_connection_socket_state, con)` at connections.R:665::
+ Caused by error in `chain_call(c_processx_connection_socket_state, con)` at connections.R:681::
! Not a socket connection @processx-connection.c:622 (processx_connection_socket_state)
diff --git a/tests/testthat/test-pipeline.R b/tests/testthat/test-pipeline.R
new file mode 100644
index 00000000..b7c177eb
--- /dev/null
+++ b/tests/testthat/test-pipeline.R
@@ -0,0 +1,260 @@
+test_that("2-process pipeline: sort | uniq", {
+ skip_on_cran()
+ skip_on_os("windows")
+
+ pl <- pipeline$new(
+ list(c("sort"), c("uniq")),
+ stdin = "|",
+ stdout = "|"
+ )
+ on.exit(pl$kill(), add = TRUE)
+
+ pl$write_input("b\na\nb\na\n")
+ pl$close_input()
+ out <- pl$read_all_output_lines()
+ pl$wait()
+
+ expect_equal(out, c("a", "b"))
+ expect_equal(pl$get_exit_statuses(), list(0L, 0L))
+})
+
+test_that("3-process pipeline: cat | sort | uniq", {
+ skip_on_cran()
+ skip_on_os("windows")
+
+ pl <- pipeline$new(
+ list(c("cat"), c("sort"), c("uniq")),
+ stdin = "|",
+ stdout = "|"
+ )
+ on.exit(pl$kill(), add = TRUE)
+
+ pl$write_input("b\na\nb\na\n")
+ pl$close_input()
+ out <- pl$read_all_output_lines()
+ pl$wait()
+
+ expect_equal(out, c("a", "b"))
+ expect_equal(pl$get_exit_statuses(), list(0L, 0L, 0L))
+})
+
+test_that("single-process pipeline is equivalent to process$new()", {
+ skip_on_cran()
+ skip_on_os("windows")
+
+ pl <- pipeline$new(list(c("echo", "hello")), stdout = "|")
+ on.exit(pl$kill(), add = TRUE)
+
+ pl$wait()
+ out <- trimws(pl$read_all_output())
+ expect_equal(out, "hello")
+ expect_equal(pl$get_exit_statuses(), list(0L))
+})
+
+test_that("pipeline is_alive() and get_pids()", {
+ skip_on_cran()
+ skip_on_os("windows")
+
+ pl <- pipeline$new(
+ list(c("sort"), c("cat")),
+ stdin = "|",
+ stdout = "|"
+ )
+ on.exit(pl$kill(), add = TRUE)
+
+ expect_true(pl$is_alive())
+ pids <- pl$get_pids()
+ expect_length(pids, 2L)
+ expect_true(all(pids > 0L))
+
+ pl$close_input()
+ pl$wait()
+ expect_false(pl$is_alive())
+})
+
+test_that("pipeline get_processes() returns process objects", {
+ skip_on_cran()
+ skip_on_os("windows")
+
+ pl <- pipeline$new(
+ list(c("sort"), c("uniq")),
+ stdin = "|",
+ stdout = "|"
+ )
+ on.exit(pl$kill(), add = TRUE)
+
+ procs <- pl$get_processes()
+ expect_length(procs, 2L)
+ expect_true(all(vapply(procs, inherits, logical(1L), "process")))
+
+ pl$close_input()
+ pl$wait()
+})
+
+test_that("pipeline kill() stops all processes", {
+ skip_on_cran()
+ skip_on_os("windows")
+
+ pl <- pipeline$new(
+ list(c("cat"), c("cat")),
+ stdin = "|",
+ stdout = "|"
+ )
+
+ expect_true(pl$is_alive())
+ pl$kill()
+ Sys.sleep(0.1)
+ expect_false(pl$is_alive())
+})
+
+test_that("pipeline stdout to file", {
+ skip_on_cran()
+ skip_on_os("windows")
+
+ tmp <- tempfile()
+ on.exit(unlink(tmp), add = TRUE)
+
+ pl <- pipeline$new(
+ list(c("sort"), c("uniq")),
+ stdin = "|",
+ stdout = tmp
+ )
+ on.exit(pl$kill(), add = TRUE)
+
+ pl$write_input("b\na\nb\na\n")
+ pl$close_input()
+ pl$wait()
+
+ expect_equal(readLines(tmp), c("a", "b"))
+ expect_equal(pl$get_exit_statuses(), list(0L, 0L))
+})
+
+test_that("conn_create_proc_pipepair() returns write/read ends", {
+ skip_on_cran()
+
+ pipe <- conn_create_proc_pipepair()
+ on.exit({
+ try(close(pipe[[1]]), silent = TRUE)
+ try(close(pipe[[2]]), silent = TRUE)
+ }, add = TRUE)
+
+ expect_length(pipe, 2L)
+ expect_true(is_connection(pipe[[1]]))
+ expect_true(is_connection(pipe[[2]]))
+})
+
+test_that("px single-process pipeline", {
+ skip_on_cran()
+ px <- get_tool("px")
+
+ pl <- pipeline$new(list(c(px, "outln", "hello")), stdout = "|")
+ on.exit(pl$kill(), add = TRUE)
+
+ pl$wait()
+ out <- trimws(pl$read_all_output())
+ expect_equal(out, "hello")
+ expect_equal(pl$get_exit_statuses(), list(0L))
+})
+
+test_that("px 2-process pipeline: passthrough", {
+ skip_on_cran()
+ px <- get_tool("px")
+
+ pl <- pipeline$new(
+ list(c(px, "cat", ""), c(px, "cat", "")),
+ stdin = "|",
+ stdout = "|"
+ )
+ on.exit(pl$kill(), add = TRUE)
+
+ pl$write_input("hello\nworld")
+ pl$close_input()
+ out <- pl$read_all_output_lines()
+ pl$wait()
+
+ expect_equal(out, c("hello", "world"))
+ expect_equal(pl$get_exit_statuses(), list(0L, 0L))
+})
+
+test_that("px 3-process pipeline: passthrough", {
+ skip_on_cran()
+ px <- get_tool("px")
+
+ pl <- pipeline$new(
+ list(
+ c(px, "cat", ""),
+ c(px, "cat", ""),
+ c(px, "cat", "")
+ ),
+ stdin = "|",
+ stdout = "|"
+ )
+ on.exit(pl$kill(), add = TRUE)
+
+ pl$write_input("hello\nworld")
+ pl$close_input()
+ out <- pl$read_all_output_lines()
+ pl$wait()
+
+ expect_equal(out, c("hello", "world"))
+ expect_equal(pl$get_exit_statuses(), list(0L, 0L, 0L))
+})
+
+test_that("px pipeline is_alive() and get_pids()", {
+ skip_on_cran()
+ px <- get_tool("px")
+
+ pl <- pipeline$new(
+ list(c(px, "cat", ""), c(px, "cat", "")),
+ stdin = "|",
+ stdout = "|"
+ )
+ on.exit(pl$kill(), add = TRUE)
+
+ expect_true(pl$is_alive())
+ pids <- pl$get_pids()
+ expect_length(pids, 2L)
+ expect_true(all(pids > 0L))
+
+ pl$close_input()
+ pl$wait()
+ expect_false(pl$is_alive())
+})
+
+test_that("px pipeline kill() stops all processes", {
+ skip_on_cran()
+ px <- get_tool("px")
+
+ pl <- pipeline$new(
+ list(c(px, "cat", ""), c(px, "cat", "")),
+ stdin = "|",
+ stdout = "|"
+ )
+
+ expect_true(pl$is_alive())
+ pl$kill()
+ pl$wait()
+ expect_false(pl$is_alive())
+})
+
+test_that("px pipeline stdout to file", {
+ skip_on_cran()
+ px <- get_tool("px")
+
+ tmp <- tempfile()
+ on.exit(unlink(tmp), add = TRUE)
+
+ pl <- pipeline$new(
+ list(c(px, "cat", ""), c(px, "cat", "")),
+ stdin = "|",
+ stdout = tmp
+ )
+ on.exit(pl$kill(), add = TRUE)
+
+ pl$write_input("hello\nworld\n")
+ pl$close_input()
+ pl$wait()
+
+ expect_equal(readLines(tmp), c("hello", "world"))
+ expect_equal(pl$get_exit_statuses(), list(0L, 0L))
+})
diff --git a/tests/testthat/test-print.R b/tests/testthat/test-print.R
index 72001ba1..960ab249 100644
--- a/tests/testthat/test-print.R
+++ b/tests/testthat/test-print.R
@@ -13,3 +13,22 @@ test_that("print", {
"PROCESS .* finished"
)
})
+
+test_that("pipeline print", {
+ skip_on_cran()
+ px <- get_tool("px")
+
+ pl <- pipeline$new(
+ list(c(px, "cat", ""), c(px, "cat", "")),
+ stdin = "|",
+ stdout = "|"
+ )
+ on.exit(pl$kill(), add = TRUE)
+
+ expect_output(print(pl), "^PIPELINE")
+ expect_output(print(pl), "\\| .* running, pid")
+
+ pl$close_input()
+ pl$wait()
+ expect_output(print(pl), "\\| .* finished")
+})