How to find correct executable with Sys.which on Windows

后端 未结 2 1503
陌清茗
陌清茗 2020-12-11 05:58

What are the workarounds on Windows to make it so Sys.which finds the proper executables? Two cases that are reoccuring problems:

  1. convert.exe

相关标签:
2条回答
  • 2020-12-11 06:12

    I asked a +/- identical question earlier this year over on R-devel. Among the replies was this one, by Henrik Bengtsson, who kindly provided the following useful function:

    Sys.which2 <- function(cmd) {
        stopifnot(length(cmd) == 1)
        if (.Platform$OS.type == "windows") {
            suppressWarnings({
                pathname <- shell(sprintf("where %s 2> NUL", cmd), intern=TRUE)[1]
            })
            if (!is.na(pathname)) return(setNames(pathname, cmd))
        }
        Sys.which(cmd)
    }
    
    ## Trying out Sys.which & Sys.which2 on my Windows box gives the following:
    Sys.which("convert")
    #                              convert 
    # "C:\\Windows\\system32\\convert.exe" 
    Sys.which2("convert")
    #                                                 convert 
    # "C:\\Program Files\\ImageMagick-6.8.8-Q16\\convert.exe" 
    

    I'm really not sure why R-core don't just fix Sys.which() to make it actually portable, but they at least do document root cause of this behavior in ?system (whose functionality is afflicted by the same problem):

    The search path for 'command' may be system-dependent: it will include the R 'bin' directory, the working directory and the Windows system directories before 'PATH'.

    0 讨论(0)
  • 2020-12-11 06:22

    Because Sys.which() is vectorized–and since I think that is useful–I've modified the Henrik Bengtsson code in the following function sys_which(), which should be a more robust and more similar version of Sys.which():

    ## check if unix
    is_unix <- function() grepl("unix", .Platform$OS.type, ignore.case = TRUE)
    
    ## robust version of Sys.which
    sys_which <- function(x) {
      if (is_unix()) {
        return(Sys.which(x))
      }
      sys_win <- function(x) {
        if (grepl("\\S", path <- Sys.which(x))) {
          return(path)
        }
        path <- tryCatch(
          suppressWarnings(system(sprintf("where %s", x), intern = TRUE)[1]),
          warning = function(w) "",
          error = function(e) "")
        if (!grepl("\\S", path)) {
          return(`names<-`("", x))
        }
        `names<-`(path, x)
      }
      vapply(x, sys_win, character(1))
    }
    

    This has the following advantages:

    1. It's vectorizedsys_which() can handle a vector (one or more input values) e.g., sys_which(c("python", "python3"))
    2. It's more error resistant–use of tryCatch(...) ensures any inputs that result in an error from the system call get passed on to the normal Sys.which() function.

    Example:

    > sys_which(c("python", "python2.7", "python3", "asdf"))
    #>            python                python2.7                  python3        asdf 
    #> "/usr/bin/python"     "/usr/bin/python2.7" "/usr/local/bin/python3"          ""
    
    0 讨论(0)
提交回复
热议问题