使用R';特定文件上的内部tar函数
R有一个方便的跨平台tar()函数,可以对tar和gzip文件执行tar和gzip操作。这个函数似乎是为了给整个目录加焦油而设计的。我希望使用此函数对目录的子集或单个文件进行tar和压缩。然而,我似乎不能这样做。我希望以下内容能够在当前工作目录中为单个csv文件添加tar:使用R';特定文件上的内部tar函数,r,tar,R,Tar,R有一个方便的跨平台tar()函数,可以对tar和gzip文件执行tar和gzip操作。这个函数似乎是为了给整个目录加焦油而设计的。我希望使用此函数对目录的子集或单个文件进行tar和压缩。然而,我似乎不能这样做。我希望以下内容能够在当前工作目录中为单个csv文件添加tar: tar( "tst.tgz", "myCsv.csv", compression="gzip" ) 那么,是否只能在目录上使用tar()函数 我通过创建一个临时目录,复制我的文件,然后给整个临时目录涂上焦油,暂时解决了这个
tar( "tst.tgz", "myCsv.csv", compression="gzip" )
那么,是否只能在目录上使用tar()函数
我通过创建一个临时目录,复制我的文件,然后给整个临时目录涂上焦油,暂时解决了这个问题。但我希望有一个更简单的解决方案。这将不需要复制文件,这对于大型文件来说有些费时 如果使用合适的模式运行
list.files()
(或其同义词dir()
)为files=
提供它要求的字符向量,会发生什么情况?我认为这里的帮助是明确的:
论据:
tarfile:tarfile:tilde扩展的路径名(请参见
将执行“path.expand”)。或者
可用于二进制写入的连接
文件:要存档的路径的字符向量:默认值
是归档当前目录下的所有文件
再次对其进行了修订,进一步简化了
tar1
代码。另外,tar1
现在可以输出一个没有目录的文件或多个没有目录的文件。本质上,我们需要解决tar中R使用list.files
的一个bug,我们通过重新定义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")
#对单个文件进行tar
tar1我认为这不可能像你描述的那样。files
参数被传递到list.files
的path
参数,因此它通过对目录中的文件(而不是单个文件)加上焦油来工作
如果您准备编辑内部函数,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
位是键。如果此IfTRUE
,list.files()
将匹配的文件名返回为“/bar.csv”
,而tar()
将粘贴在tarball内的文件夹中。如果我们将其设置为FALSE
,list.files()
返回“bar.csv”
,那么我们将根据请求获得一个带有单个csv文件的tarball
如果您有名称相似的文件,并且只希望找到指定的文件名,请在模式中用^
和$
将其固定,例如:
tar2("foo.tar", path = ".", pattern = "^bar.csv$", recursive = FALSE,
full.names = FALSE, all.files = FALSE)
下面是修改后的tar()
函数作为tar2()
:
tar2是的,我的问题应该更详细一些。我从那开始…让我把问题改成一个文件的简单情况,这是我开始问的,但后来转到了通配符情况中间的问题。我感觉我试图让tar()做一些它不打算做的事情。感谢您的确认;)@JD Long如果您编辑tar()
函数以接受不同的参数,则可以执行此操作。请看我更新的答案中的一个解决方案。在看了这个之后,我一直在考虑是否使用补丁来接近R-devel。这将使tar()
更加有用。不过,在提出之前,我需要先做一点检查。@Gavin Simpson,我认为提交它是值得的,但我认为应该简化接口,只是为了有一个files参数,而不是tar
当前没有的额外参数。这些额外的参数使其更易于实现,但对用户来说并不方便。“无论如何,我已经给你的职位投了一票,”格洛森迪耶克同意。我发布的tar2()
只是@JD可以利用的东西。我需要按照你提到的思路考虑一个提议的改变list.files()
无法使用我们可能希望使用的tar,因此它可能需要一些修改以避免额外的参数。它在我的系统上工作。我已将输出添加到帖子中以显示。@JD Long将他的Q简化为试图对单个声明的csv文件进行tar。我已按照答案第一句中的讨论修改了答案。感谢您的回答。我需要研究tar()函数,然后研究你的答案,以便完全理解这里发生的事情
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)
}