R语言无网络安装R包,彻底解决依赖问题!

R version: 3.5.3, 3.6.3
更新日期: 2020-9-10

大家测试后多提建议哈, 有问题我会持续更新的

在工作中,我们使用的服务器通常是不能联外网的,这在安装R包的时候产生了巨大的不便。网上有很多帖子使用tools::package_dependencies这个工具下载依赖,但是这个工具是有坑的,相信尝试过的同学依然会发现有些依赖包在下载时被漏掉了,查了很多帖子,这个问题一直没有很好的解决。

今天,我就来解决这个问题,一来,方便自己,二来,服务他人。

我们就用R自己来解决自己的问题吧!

 

在本地有网络的环境中下载需要的R包:

library(rvest)
library(stringi)
library(stringr)

dir <- 'c:/work/R/packages/' # 设置一个空目录存放R包
pknames <- c('Seurat') # 这是想要安装的包名称,可以设定多个包哦

第一个函数,用于生成repo的下载地址:

get_addr <- function(name,repo='https://cloud.r-project.org/'){
  addr <- paste0(repo,'web/packages/',name,'/index.html')
  return(addr)
}

第二个函数,获得一个包的次级依赖:

get_dep <- function(name){
  addr <- get_addr(name)
  gettry <- try(page <- read_html(addr),silent = T)
  if('try-error' %in% class(gettry)){
    return('-')
  }
  gettry <- try(pkg_table <- page %>% html_node("table") %>% html_table(fill = TRUE),silent = T)
  if('try-error' %in% class(gettry)){
    return('-')
  }
  dep_pkgs1 <- c()
  dep_pkgs2 <- c()
  if(length(which(pkg_table[,1]=='Imports:'))>0 ){
    tmp <- str_replace_all(pkg_table[which(pkg_table[,1]=='Imports:'),2],'\\(.*?\\)','')
    tmp <- str_split(tmp,'\\,')[[1]]
    tmp <- str_replace_all(tmp,'\\(.*\n.*\\)','')
    dep_pkgs1 <- trimws(tmp, which = c("both", "left", "right"))
    # return(dep_pkgs)
  }
  if( length(which(pkg_table[,1]=='LinkingTo:'))>0 ){
    tmp <- str_replace_all(pkg_table[which(pkg_table[,1]=='LinkingTo:'),2],'\\(.*?\\)','')
    tmp <- str_split(tmp,'\\,')[[1]]
    tmp <- str_replace_all(tmp,'\\(.*\n.*\\)','')
    dep_pkgs2 <- trimws(tmp, which = c("both", "left", "right"))
  }
  if( length(dep_pkgs1)>0 & length(dep_pkgs2)>0 ){
    return( c(dep_pkgs1,dep_pkgs2) )
  }else if(length(dep_pkgs1)>0 & length(dep_pkgs2)==0){
    return( dep_pkgs1 )
  }else if(length(dep_pkgs1)==0 & length(dep_pkgs2)>0){
    return( dep_pkgs2 )
  }else{
    return('-')
  }
}

第三个函数,获得需要安装的所有包的全部依赖:

get_all_dep <- function(pknames){
  all_list <- c()
  all_list <- c(all_list,pknames)
  top <- 1
  for (i in 1:length(all_list)) {
    one <- get_dep(all_list[i])
    if(all(one != '-')){
      all_list <- c(all_list,one)
    }
    top <- top + 1
  }
  
  while(top <= length(all_list)){
    cat('finding dep of',all_list[top],'...\n')
    cat( 'length=',length(all_list),'\n' )
    cat( 'top=',top,'\n' )
    
    if( all_list[top] %in% all_list[(top+1):length(all_list)] ){
      top <- top + 1
      next
    }else{
      one <- get_dep(all_list[top])
      if(length(one) == 1 && one == '-'){
        top <- top + 1
        next
      }else{
        all_list <- c(all_list,one)
        top <- top + 1
      }
    }
  }
  
  res_list <- c()
  for (i in length(all_list):1) {
    if( ! all_list[i] %in% res_list ){
      res_list <- c(all_list[i],res_list)
    }
  }
  
  return(res_list)
}

好了,现在在联网的环境下调用这个函数:

res <- get_all_dep(pknames) # 不要管报错,没啥问题

现在下载res中的记录的包,路径就是dir

download.packages(res,destdir = dir)
split_list <- str_split(list.files(dir),'_')
download_pkgs1 <- unlist(split_list)[seq(1,length(split_list)*2,2)]
download_pkgs2 <- unlist(split_list)[seq(2,length(split_list)*2,2)]
res <- cbind(res,NA)
for (i in 1:length(res[,1])) {
  if( res[i,1] %in% download_pkgs1 ){
    name <- download_pkgs1[which(download_pkgs1 == res[i,1])]
    version <- download_pkgs2[which(download_pkgs1 == res[i,1])]
    res[i,2] <- paste0(name,'_',version )
  }
}
save(res,file= paste0(dir,'install_list.RData') )

本地的工作结束了,现在将dir目录打包上传到服务器,用服务器上的R运行以下代码,将服务器对应的目录设置为wdir

wdir <- '/home/you/packages/'
load(file = paste0(wdir,'install_list.RData'))
installed_packages <- row.names(installed.packages())
for (i in length(res[,1]):1) {
  if( res[i,1] %in% installed_packages | is.na(res[i,2]) ){
    next
  }else{
    install.packages(pkgs=paste0(wdir,res[i,2]),repos = NULL,type = 'source')
  }
  # a = readline('continue?')
  # if(a != ''){
  #  break
  # }
  # 这里可以注释掉,我要装131个包,一个个敲回车太累了,可以先运行一遍,再去掉注释运行一遍,以防某些依赖库缺失的情况
}

不出意外的话,所有的包就装好了,反正我的好了哈哈,以后就用这个啦。
 
 

posted on 2020-09-02 22:17  葬花朴  阅读(1517)  评论(0编辑  收藏  举报