Let´s assume that all the non-base R functions in a script are called by package::function()
. Therefore, it don´t have any full package load during the script run. Let´s assume that we have the a R script called run.R
with the following contents.
"data.table::fread(file)"
In this example, the script would need the data.table
package. I am looking for a R function that reading this external run.R
script would give the code to install all the requested packages ie:
install.packages("data.table")
Any idea about existent functions or strategies?
You could try something like this using regex:
f <- file("/path/to/here/file.R") # set up connection to file
file_lines <- readLines(con = f) # read file into list
close(f)
pckgs <- lapply(file_lines, function(l) {
if(grepl("::", l)){
gsub(".*?([[:alnum:]\\.]+)::.*","\\1", l)
} else {
return(NULL)
}
})
unique(unlist(pckgs))
I worked under the assumption that package names only contains letters and numbers. you may need to change the regex pattern if that isn't the case.
Update: changed the assumption to include a .
as per the data.table
example
The other solutions using regular expressions will also match ::
if it occurs in a comment or string literal. It's better to parse the script and look for things that parse to the pkg::fn
operation. For example:
src <- "data.table::fread(file)"
# Use src <- readLines("source.R") in the real case, or parse the file directly
parsed <- parse(text = src)
parseData <- getParseData(parsed)
parseData$text[parseData$token == "SYMBOL_PACKAGE"]
#> [1] "data.table"
Edited to add: You can put this in a function to install necessary packages before running a script. For example, if these lines are in ~/temp/run.R
:
file <- "not::a::package"
data.table::fread(file)
foobar::notafunction()
then you get these results:
installThenSource <- function(file, ...) {
parsed <- parse(file)
parseData <- getParseData(parsed)
packages <- unique(parseData$text[parseData$token == "SYMBOL_PACKAGE"])
for (p in packages) {
if (!requireNamespace(p, quietly = TRUE)) {
message("Installing ", p)
install.packages(p)
if (!requireNamespace(p, quietly = TRUE))
stop("Install of ", p, " failed.")
} else
message("Package ", p, " already installed.")
}
source(file, ...)
}
installThenSource("~/temp/run.R")
#> Package data.table already installed.
#> Installing foobar
#> Warning: package 'foobar' is not available (for R version 3.6.1)
#> Error in installThenSource("~/temp/run.R"): Install of foobar failed.
Here is a function that finds strings like package::function
.
findPackages <- function(file){
txt <- readLines(file)
inx <- grep('::', txt)
txt <- txt[inx]
m <- regexpr('[[:alnum:]]+::', txt)
pkg <- regmatches(txt, m)
unique(sub('::', '', pkg))
}
This Ubuntu bash command gets all files *.R
in the working directory with ::
in them.
fls <- system2('grep', args = c('-l', '::', '*.R'), stdout = TRUE)
Now apply the function to a file that calls functions like that.
findPackages(fls[1])
And to all such files found with the bash command.
pkgs <- lapply(fls, findPackages)
unique(unlist(pkgs))
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.