rtarsilent

How to silent the output of `untar(files="xxx")` when a file is not found?


I am using utils::untar() with its argument files to extract a single file.

However, if the file is missing, I get a warning and a non-stopping internal error message.

I could remove the warning using suppressWarnings(), but how can I remove the error messages?

I'm running R 4.1.3 on Windows 10.

Reproducible Example:

Extract one existing file -> ALL RIGHT

library(glue)
tmp = tempdir()
target = glue("{tmp}/spelling_2.2.tar.gz")
download.file("https://cran.r-project.org/src/contrib/Archive/spelling/spelling_2.2.tar.gz", destfile=target)

untar(target, files="spelling/DESCRIPTION", exdir=glue("{tmp}/extract1_ok"))
list.files(glue("{tmp}/extract1_ok"), recursive=TRUE) |> head()
#> [1] "spelling/DESCRIPTION"
list.dirs(glue("{tmp}/extract1_ok"), recursive=TRUE, full.names=FALSE) |> head()
#> [1] ""         "spelling"

Extract one missing file -> PROBLEM: unsilencable messages

untar(target, files="xxx.zzz", exdir=glue("{tmp}/extract1_missing"))
#> tar.exe: xxx.zzz: Not found in archive
#> tar.exe: Error exit delayed from previous errors.
#> Warning in untar(target, files = "xxx.zzz", exdir = glue("{tmp}/extract1_missing")): 'tar.exe -xf
#> "C:\Users\Dan\AppData\Local\Temp\RtmpQ3jdcY/spelling_2.2.tar.gz" -C
#> "C:/Users/Dan/AppData/Local/Temp/RtmpQ3jdcY/extract1_missing" "xxx.zzz"'
#> returned error code 1
list.files(glue("{tmp}/extract1_missing"), recursive=TRUE) |> head()
#> character(0)
list.dirs(glue("{tmp}/extract1_missing"), recursive=TRUE, full.names=FALSE) |> head()
#> [1] ""

The warnings can be suppressed using suppressWarnings(), but the tar.exe: ... cannot.

EDIT:

Using tar="internal" is a solution, but it has the side effect of creating empty dirs, which is somehow unwanted. This function will be called a tremendous lot, so removing those dirs would be a bit slow.

Extract anything with tar="internal" -> PROBLEM: annoying empty dirs


untar(target, files="spelling/DESCRIPTION", exdir=glue("{tmp}/extract_internal"), 
      tar="internal")
list.files(glue("{tmp}/extract_internal"), recursive=TRUE) |> head()
#> [1] "spelling/DESCRIPTION"
list.dirs(glue("{tmp}/extract_internal"), recursive=TRUE, full.names=FALSE) |> head()
#> [1] ""                        "spelling"               
#> [3] "spelling/inst"           "spelling/inst/templates"
#> [5] "spelling/man"            "spelling/R"

Solution

  • Use system or system2 to run your system tar, then you will have the options to ignore stderr:

    tmp <- tempdir()
    target <- file.path(tmp, "spelling_2.2.tar.gz")
    download.file("https://cran.r-project.org/src/contrib/Archive/spelling/spelling_2.2.tar.gz", destfile = target)
    
    exdir <- file.path(tmp, "extract1_ok")
    files <- "spelling/DESCRIPTION"
    dir.create(exdir, showWarnings = FALSE, recursive = TRUE)
    if (system2("tar", c("-xf", target, "-C", exdir, files), stderr = NULL)) unlink(exdir, recursive = TRUE)
    list.files(exdir, recursive = TRUE) |> head()
    list.dirs(exdir, recursive = TRUE, full.names = FALSE) |> head()
    
    exdir <- file.path(tmp, "extract1_missing")
    files <- "xxx.zzz"
    dir.create(exdir, showWarnings = FALSE, recursive = TRUE)
    if (system2("tar", c("-xf", target, "-C", exdir, files), stderr = NULL)) unlink(exdir, recursive = TRUE)
    list.files(exdir, recursive = TRUE) |> head()
    list.dirs(exdir, recursive = TRUE, full.names = FALSE) |> head()
    

    If you View(untar), you are going to see that untar function construct the call to pass to system to run the system tar program.