readArgs: Read/Write Command-Line Arguments from/to a File

readArgsR Documentation

Read/Write Command-Line Arguments from/to a File

Description

When providing arguments to an R script, it is often necessary to quote the arguments such that they are interpreted correctly by the shell before being passed to the script. However, this comes with the issue of different quoting conventions for different shells, meaning that the strings will not be interpreted the same between different shells. Additionally, some characters may not be allowed for certain shells.

You can get around this with writeArgs which will right your arguments (and comments!) to a file, and returns a filename that can be used in the arguments place.

readArgs is the corresponding function to read those arguments back into R.

Usage

writeArgs(x, file = tempfile(pattern = pattern, fileext = fileext),
    pattern = "args", fileext = ".Rargs", comments = TRUE,
    nlines.between.comment.and.args = 0, nlines.between.args = 2,
    at = TRUE, name = NULL)

readArgs(file, name = NULL)

Arguments

x

any R object. If a list, each element will be turned into a character vector (in the same way as withArgs converts its arguments), otherwise will be turned into a character vector (same as withArgs). The arguments to be written.

file

character string or NULL. Name of the file to be read from, or "" to write to stdout() (intended for verifying the output looks as expected), or NULL to return the formatted text without writing anywhere.

pattern, fileext

character string. Allows for easier means of editing the filename created.

comments

logical. Should the comments of x be written as well?

nlines.between.comment.and.args

a non-negative integer specifying the number of empty lines between each set of comments and arguments.

nlines.between.args

a non-negative integer specifying the number of empty lines between each set of arguments. Only used for a list x.

at

logical. Should an "@" character be placed before the returned filename?

name

The name of the format for reading/writing, see Details. For writing, will be ignored if file is a non-empty character string.

Details

readArgs and writeArgs accept many formats for reading and writing arguments, based on the file extension.

R arguments, extension ".Rargs"

Arguments are read as though they were R strings, separated by newline "\n" or semicolon " ; ". This means arguments must be quoted and can use any of the escape sequences seen in Quotes. The most reliable method, also the slowest (but not by much). Can contain R comments, will be ignored when read.

Python arguments, extension ".pyargs"

Arguments are read one per line. No quoting rules apply. Arguments cannot contain newline or carriage return, and will not be read/written properly if so. The fastest method. Cannot contain comments.

Comma-separated arguments, extension ".csv"

Arguments are read using scan, delimited/separated by comma ",". Arguments may be quoted with double quotes "\"", and must be quoted if they contain comma, newline, or double quotes. Arguments cannot contain carriage return. Cannot contain comments.

Tab-separated arguments, extension ".tsv"

Arguments are read using scan, delimited/separated by tab "\t". Arguments may be quoted with double quotes "\"", and must be quoted if they contain tab, newline, or double quotes. Arguments cannot contain carriage return. Cannot contain comments.

Other arguments, other extension

Arguments are read using scan, delimited/separated by ‘white-space’. Arguments may be quoted with single quotes "'" and double quotes "\"", and must be quoted if they contain white-space, single quotes, or double quotes. Arguments cannot contain carriage return. Quoted arguments cannot contain an odd number of trailing backslashes, nor an odd number of backslashes before an embedded quoting character. Can contain comments, marked by a non-quoted hash character "#".

The methods listed above can read/write from compressed files (see gzfile). For "csv", "tsv", and "other" methods, writeArgs tries to space the text such that the file will look nice to open within a text editor and ‘Excel’. Each line of arguments (not comments) will have at most 80 characters (to look nice in a text editor) or at most 10 columns (to look nice in ‘Excel’), whichever comes first (though this rule is broken by strings with more than 80 characters).

Value

for file = NULL, the formatted text.

for file = "", the formatted text invisibly.

otherwise, a character string naming a file containing your arguments.

Examples

x <- letters; essentials::writeArgs(x, file = "")


comment(x) <- essentials::dedent("
    adding a comment
    to our arguments
"); essentials::writeArgs(x, file = "")


x <- list(


    local({
        x <- c("\xC5", "\xC9", "\xD8", "\xEC", "\xFC")
        Encoding(x) <- "latin1"
        comment(x) <- "accented characters"
        x
    }),


    local({
        x <- c("\u{03C3}", "\u{03B4}")
        comment(x) <- 'greek letters (default encoding "UTF-8")'
        x
    }),


    local({
        x <- "fa\xE7ile"
        Encoding(x) <- "latin1"  # x is intended to be in latin1
        comment(x) <- essentials::dedent(r"{
            another non-ASCII character ("unknown" in UTF-8 locale,
            "latin1" in IS08859-1, ...)
        }")
        x
    }),


    local({
        x <- c("\u{7B90}", "\u{5316}", "\u{5B57}")
        comment(x) <- 'chinese characters (default encoding "UTF-8")'
        x
    }),


    local({
        x <- c("\U{0001D11E}", "\U{0001D4D7}")
        comment(x) <- "rarer characters outside the usual 16^4 range"
        x
    }),


    local({
        x <- essentials::dedent(r"{
            this would be 'rather' annoying to quote for a `shell` on $Unix$,
            and even "more" so on Windows because of the \"double\" quotes!
        }")
        comment(x) <- "all ASCII characters, annoying to quote, hard to read"
        x
    })
)
comment(x) <- essentials::dedent("
    these are some unusual characters
    but should still behave correctly
")


# the same arguments in different formats
essentials::writeArgs(x, "", name = "Rargs")
essentials::writeArgs(x, "", name = "pyargs")
essentials::writeArgs(x, "", name = "csv")
essentials::writeArgs(x, "", name = "tsv")
essentials::writeArgs(x, "", name = NULL)


FILE <- essentials::writeArgs(x, at = FALSE)
y <- essentials::readArgs(FILE)


# for the purpose of comparison, we need 'x' to be a character vector
z <- this.path::asArgs(x)


# let's check that the arguments in 'x' match the
# arguments written to and read back from 'FILE' (in 'y')
#
# we don't use 'identical(x, y)' because 'x' has
# attributes (a comment) while 'y' does not
if (length(z) != length(y)) {
    cat(gettextf("Catastrophic failure, wrote %d arguments, read %d\n",
        length(z), length(y)),
        file = stderr())
    stop("Please submit a bug report using ",
        "utils::bug.report(package = \"essentials\")")
} else if (any(i <- z != y)) {
    cat(ngettext(sum(i),
        "The following argument was written or read incorrectly!\n",
        "The following arguments were written or read incorrectly!\n"),
        file = stderr())
    print(z[i])
    cat("\nIncorrectly written or read as:\n", file = stderr())
    print(y[i])
    stop("Please submit a bug report using ",
        "utils::bug.report(package = \"essentials\")")
} else cat("Yay! The arguments were written and read correctly!\n")





# Using writeArgs and withArgs, Rscript
cat(
    essentials::dedent(r"{
        withAutoprint({
            parser <- essentials::ArgumentParser()
            parser$add.argument("args", nargs = "*")
            pargs <- parser$parse.args()
            print(pargs$args)
        })
    }"),
    file = script <- tempfile(), sep = "\n"
)
this.path::withArgs(
    source(script, local = TRUE, echo = FALSE),
    paste0("@", FILE)
)
essentials::Rscript("--default-packages=NULL", script, args = paste0("@", FILE))





# reading/writing from/to a compressed file
FILE2 <- essentials::writeArgs(x, at = FALSE, fileext = ".Rargs.gz")
stopifnot(identical(
    essentials::readArgs(FILE),
    essentials::readArgs(FILE2)
))


# miniscule difference, more desirable with more arguments
c(file.size.original   = file.size(FILE),
  file.size.compressed = file.size(FILE2))

ArcadeAntics/essentials documentation built on Nov. 7, 2024, 4:33 p.m.