[r] How to include (source) R script in other scripts

I've created a utility R script, util.R, which I want to use from other scripts in my project. What is the proper way to ensure that the function this script defines are available to function in my other scripts?

I'm looking for something similar to the require function, that loads a package only if it has not been loaded yet. I don't want to call source("util.R") because that will load the script every time it is called.

I know that I will get some answers telling me to create a package, as in Organizing R Source Code :) But I'm not creating something that will be used elsewhere, it is just a standalone project.

This question is related to r

The answer is


Here is one possible way. Use the exists function to check for something unique in your util.R code.

For example:

if(!exists("foo", mode="function")) source("util.R")

(Edited to include mode="function", as Gavin Simpson pointed out)


Say util.R produces a function foo(). You can check if this function is available in the global environment and source the script if it isn't:

if(identical(length(ls(pattern = "^foo$")), 0))
    source("util.R")

That will find anything with the name foo. If you want to find a function, then (as mentioned by @Andrie) exists() is helpful but needs to be told exactly what type of object to look for, e.g.

if(exists("foo", mode = "function"))
    source("util.R")

Here is exists() in action:

> exists("foo", mode = "function")
[1] FALSE
> foo <- function(x) x
> exists("foo", mode = "function")
[1] TRUE
> rm(foo)
> foo <- 1:10
> exists("foo", mode = "function")
[1] FALSE

Here is a function I wrote. It wraps the base::source function to store a list of sourced files in a global environment list named sourced. It will only re-source a file if you provide a .force=TRUE argument to the call to source. Its argument signature is otherwise identical to the real source() so you don't need to rewrite your scripts to use this.

warning("overriding source with my own function FYI")
source <- function(path, .force=FALSE, ...) {
  library(tools)
  path <- tryCatch(normalizePath(path), error=function(e) path)
  m<-md5sum(path)

  go<-TRUE
  if (!is.vector(.GlobalEnv$sourced)) {
    .GlobalEnv$sourced <- list()
  }
  if(! is.null(.GlobalEnv$sourced[[path]])) {
    if(m == .GlobalEnv$sourced[[path]]) {
      message(sprintf("Not re-sourcing %s. Override with:\n  source('%s', .force=TRUE)", path, path))
      go<-FALSE
    }
    else {
      message(sprintf('re-sourcing %s as it has changed from: %s to: %s', path, .GlobalEnv$sourced[[path]], m))
      go<-TRUE
    }
  } 
  if(.force) {
    go<-TRUE
    message("  ...forcing.")
  }
  if(go) {
    message(sprintf("sourcing %s", path))
    .GlobalEnv$sourced[path] <- m
    base::source(path, ...)
  }
}

It's pretty chatty (lots of calls to message()) so you can take those lines out if you care. Any advice from veteran R users is appreciated; I'm pretty new to R.


I solved my problem using entire address where my code is: Before:

if(!exists("foo", mode="function")) source("utils.r")

After:

if(!exists("foo", mode="function")) source("C:/tests/utils.r")

There is no such thing built-in, since R does not track calls to source and is not able to figure out what was loaded from where (this is not the case when using packages). Yet, you may use same idea as in C .h files, i.e. wrap the whole in:

if(!exists('util_R')){
 util_R<-T

 #Code

}

You could write a function that takes a filename and an environment name, checks to see if the file has been loaded into the environment and uses sys.source to source the file if not.

Here's a quick and untested function (improvements welcome!):

include <- function(file, env) {
  # ensure file and env are provided
  if(missing(file) || missing(env))
    stop("'file' and 'env' must be provided")
  # ensure env is character
  if(!is.character(file) || !is.character(env))
    stop("'file' and 'env' must be a character")

  # see if env is attached to the search path
  if(env %in% search()) {
    ENV <- get(env)
    files <- get(".files",ENV)
    # if the file hasn't been loaded
    if(!(file %in% files)) {
      sys.source(file, ENV)                        # load the file
      assign(".files", c(file, files), envir=ENV)  # set the flag
    }
  } else {
    ENV <- attach(NULL, name=env)      # create/attach new environment
    sys.source(file, ENV)              # load the file
    assign(".files", file, envir=ENV)  # set the flag
  }
}