【问题标题】:using R's internal tar function on specific files在特定文件上使用 R 的内部 tar 函数
【发布时间】:2011-02-17 20:22:07
【问题描述】:

R 有一个方便的跨平台 tar() 函数,可以 tar 和 gzip 文件。似乎此功能旨在压缩整个目录。我希望使用此功能来 tar 和压缩目录的子集或单个文件。但是,我似乎无法做到这一点。我期待以下内容在当前工作目录中压缩单个 csv 文件:

tar( "tst.tgz", "myCsv.csv", compression="gzip" )

那么是否只能对目录使用 tar() 函数?

我通过创建一个临时目录、复制我的文件,然后对整个临时目录进行 tar 来临时解决这个问题。但我希望有一个更简单的解决方案。这不需要复制文件,这对于大文件来说有些耗时。

【问题讨论】:

  • 如果你只有一个文件,为什么不使用gzfile?
  • 我真的想做更多...但愿意从一个开始

标签: r tar


【解决方案1】:

我认为你描述的 JD 是不可能的。 files 参数被传递给 list.filespath 参数,因此它通过在目录中压缩文件而不是单个文件来工作。

如果你准备编辑一个内部函数,tar() 可以做你想做的事,方法是在tar() 中调用list.files()。稍微摆弄了一下,产生了下面的tar2() 函数,它有额外的参数来控制list.files() 返回的内容。使用这个函数,我们可以通过这样的调用实现你想要的:

tar2("foo.tar", path = ".", pattern = "bar.csv", recursive = FALSE, 
     full.names = FALSE, all.files = FALSE)

all.files = FALSE 可能是多余的,除非您有名称包含 "bar.csv" 的隐藏文件。

recursive = FALSE 位只会停止函数搜索除当前目录之外的任何位置,这似乎是您想要的,如果工作目录有很多文件和子文件夹,则可以加快搜索速度。

full.names = FALSE 位是关键。如果TRUE, list.files() 将匹配的文件名返回为"./bar.csv",则tar() 将保留在压缩包内的文件夹中。如果我们将其设置为 FALSElist.files() 将返回 "bar.csv",因此我们会根据要求获得一个包含单个 CSV 文件的 tarball。

如果您有名称相似的文件,并且希望只找到指定的文件名,请将其与^$ 挂钩,例如:

tar2("foo.tar", path = ".", pattern = "^bar.csv$", recursive = FALSE, 
     full.names = FALSE, all.files = FALSE)

这里是修改后的tar()函数为tar2()

tar2 <- function (tarfile, files = NULL, compression = c("none", "gzip", 
    "bzip2", "xz"), compression_level = 6, tar = Sys.getenv("tar"),
    pattern = NULL, all.files = TRUE, recursive = TRUE, full.names = TRUE) 
{
    if (is.character(tarfile)) {
        TAR <- tar
        if (nzchar(TAR) && TAR != "internal") {
            flags <- switch(match.arg(compression), none = "cf", 
                gzip = "zcf", bzip2 = "jcf", xz = "Jcf")
            cmd <- paste(TAR, flags, shQuote(tarfile), paste(shQuote(files), 
                collapse = " "))
            return(invisible(system(cmd)))
        }
        con <- switch(match.arg(compression), none = file(tarfile, 
            "wb"), gzip = gzfile(tarfile, "wb", compress = compression_level), 
            bzip2 = bzfile(tarfile, "wb", compress = compression_level), 
            xz = xzfile(tarfile, "wb", compress = compression_level))
        on.exit(close(con))
    }
    else if (inherits(tarfile, "connection")) 
        con <- tarfile
    else stop("'tarfile' must be a character string or a connection")
    files <- list.files(files, recursive = recursive, all.files = all.files, 
        full.names = full.names, pattern = pattern)
    bf <- unique(dirname(files))
    files <- c(bf[!bf %in% c(".", files)], files)
    for (f in unique(files)) {
        info <- file.info(f)
        if (is.na(info$size)) {
            warning(gettextf("file '%s' not found", f), domain = NA)
            next
        }
        header <- raw(512L)
        if (info$isdir && !grepl("/$", f)) 
            f <- paste(f, "/", sep = "")
        name <- charToRaw(f)
        if (length(name) > 100L) {
            if (length(name) > 255L) 
                stop("file path is too long")
            s <- max(which(name[1:155] == charToRaw("/")))
            if (is.infinite(s) || s + 100 < length(name)) 
                stop("file path is too long")
            warning("storing paths of more than 100 bytes is not portable:\n  ", 
                sQuote(f), domain = NA)
            prefix <- name[1:(s - 1)]
            name <- name[-(1:s)]
            header[345 + seq_along(prefix)] <- prefix
        }
        header[seq_along(name)] <- name
        header[101:107] <- charToRaw(sprintf("%07o", info$mode))
        uid <- info$uid
        if (!is.null(uid) && !is.na(uid)) 
            header[109:115] <- charToRaw(sprintf("%07o", uid))
        gid <- info$gid
        if (!is.null(gid) && !is.na(gid)) 
            header[117:123] <- charToRaw(sprintf("%07o", gid))
        size <- ifelse(info$isdir, 0, info$size)
        header[137:147] <- charToRaw(sprintf("%011o", as.integer(info$mtime)))
        if (info$isdir) 
            header[157L] <- charToRaw("5")
        else {
            lnk <- Sys.readlink(f)
            if (is.na(lnk)) 
                lnk <- ""
            header[157L] <- charToRaw(ifelse(nzchar(lnk), "2", 
                "0"))
            if (nzchar(lnk)) {
                if (length(lnk) > 100L) 
                  stop("linked path is too long")
                header[157L + seq_len(nchar(lnk))] <- charToRaw(lnk)
                size <- 0
            }
        }
        header[125:135] <- charToRaw(sprintf("%011o", as.integer(size)))
        header[258:262] <- charToRaw("ustar")
        header[264:265] <- charToRaw("0")
        s <- info$uname
        if (!is.null(s) && !is.na(s)) {
            ns <- nchar(s, "b")
            header[265L + (1:ns)] <- charToRaw(s)
        }
        s <- info$grname
        if (!is.null(s) && !is.na(s)) {
            ns <- nchar(s, "b")
            header[297L + (1:ns)] <- charToRaw(s)
        }
        header[149:156] <- charToRaw(" ")
        checksum <- sum(as.integer(header))%%2^24
        header[149:154] <- charToRaw(sprintf("%06o", as.integer(checksum)))
        header[155L] <- as.raw(0L)
        writeBin(header, con)
        if (info$isdir || nzchar(lnk)) 
            next
        inf <- file(f, "rb")
        for (i in seq_len(ceiling(info$size/512L))) {
            block <- readBin(inf, "raw", 512L)
            writeBin(block, con)
            if ((n <- length(block)) < 512L) 
                writeBin(raw(512L - n), con)
        }
        close(inf)
    }
    block <- raw(512L)
    writeBin(block, con)
    writeBin(block, con)
    invisible(0L)
}

【讨论】:

  • 我觉得我试图让 tar() 做一些它不打算做的事情。感谢您的确认;)
  • @JD Long 如果您编辑 tar() 函数以接受不同的参数,则可以做到这一点。请参阅我更新的答案以获取一种解决方案。
  • 看了这个之后,我一直在考虑是否要使用补丁来接近 R-devel。它会让tar() 更加有用。不过,在提出建议之前,我需要先做更多检查。
  • @Gavin Simpson,我认为提交它是值得的,但我认为应该简化接口,只是为了有一个 files 参数,而不是 tar 目前没有的额外参数。这些额外的参数使实现更容易,但对用户来说并不方便。无论如何,我已经给你的帖子投了赞成票。
  • @G.格洛腾迪克同意了。我发布的tar2() 只是@JD 可以使用的东西。不过,我需要考虑按照您提到的方式提出的更改; list.files() 不能像我们希望使用的那样工作 tar 因此可能需要进行一些修改以避免额外的参数。
【解决方案2】:

再次对此进行了修订,进一步简化了tar1 代码。 tar1 现在也可以输出一个没有目录的文件或多个没有目录的文件。本质上,我们需要解决 R 在 tar 中使用 list.files 的错误,我们通过重新定义 tar1 使用的 list.files 来做到这一点。

操作tar 环境的行实际上创建了tar 的副本,其环境是tar1 中的环境,因此当运行复制的tar 时,它首先会在那里查找list.files。如果我们没有在新环境中复制tar,那么它将在R 的基础上使用list.files,而忽略我们的重新定义。

tar1 下面是tar 命令的变体,它生成一个 tar 文件,其组件具有一个级别(即单个文件或一组没有目录的文件)。假设所有文件都在当前目录中。

按照tar1 的定义,我们通过创建两个文件并使用其中的第一个文件然后使用这两个文件创建存档来对其进行测试。

# tar a single file
tar1 <- function(...) {
    list.files <- function(...) ..1
    environment(tar) <- environment()
    tar(...)
}

# test - first create test files, then some test runs of tar1
cat("a", file = "a.csv")
cat("b", file = "a.csv")

tar1("tst.tgz", "a.csv", "gzip")
tar1("tst2.tgz", Sys.glob("*.csv"), "gzip")

【讨论】:

  • 它适用于我的系统。我已将输出添加到帖子中以显示它。
  • @JD Long 将他的 Q 简化为尝试 tar 一个声明的 csv 文件。
  • 我已经修改了答案第一句中讨论的答案。
  • 感谢您的回答。我需要研究 tar() 函数,然后研究你的答案,以便完全掌握这里发生的事情。
  • 我添加了更多评论并进一步简化了它,希望它更容易理解。
【解决方案3】:

如果你给files=它要求的字符向量,比如通过运行list.files()(或其同义词dir()),会发生什么?我认为那里的帮助很明确:

参数:

tarfile:tarfile 的路径名:波浪号扩展(参见 ‘path.expand’) 将被执行。或者,一个 可用于二进制写入的连接。

files:要归档的文件路径的字符向量:默认值 就是将当前目录下的所有文件归档。

【讨论】:

  • 是的,我的问题应该更详细一些。我从那个开始...让我将问题更改为单个文件的简单大小写,这是我开始问的问​​题,但后来在问题中切换到通配符大小写。
猜你喜欢
  • 2021-05-09
  • 1970-01-01
  • 2022-11-19
  • 2016-05-28
  • 1970-01-01
  • 2023-04-09
  • 2017-05-15
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多