diff --git a/NEWS.md b/NEWS.md index 7cf63f0b7..aff36d4f4 100644 --- a/NEWS.md +++ b/NEWS.md @@ -40,6 +40,8 @@ 5. Non-equi joins combining an equality condition with two inequality conditions on the same column (e.g., `on = .(id == id, val >= lo, val <= hi)`) no longer error, [#7641](https://github.com/Rdatatable/data.table/issues/7641). The internal `chmatchdup` remapping of duplicate `rightcols` was overwriting the original column indices, causing downstream code to reference non-existent columns. Thanks @tarun-t for the report and fix, and @aitap for the diagnosis. +6. `fread("clipboard")` now works on Linux (via `xclip`, `xsel`, or `wl-paste`), macOS (via `pbpaste`), and Windows, [#1292](https://github.com/Rdatatable/data.table/issues/1292). Thanks @mbacou for the report, @ben-schwen for the suggestion, and @AmanKashyap0807 for the fix. + ### Notes 1. {data.table} now depends on R 3.5.0 (2018). diff --git a/R/fread.R b/R/fread.R index bc8509c71..a6e73bbb8 100644 --- a/R/fread.R +++ b/R/fread.R @@ -63,6 +63,36 @@ yaml=FALSE, tmpdir=tempdir(), tz="UTC") # input is data itself containing at least one \n or \r } else if (startsWith(input, " ")) { stopf("input= contains no \\n or \\r, but starts with a space. Please remove the leading space, or use text=, file= or cmd=") + } else if (grepl("^clipboard(-[0-9]+)?$", tolower(input))) { + os_type = .Platform$OS.type + if (identical(os_type, "windows")) { + clip = tryCatch(utils::readClipboard(), + error = function(e) stopf("Reading clipboard failed on Windows: %s", conditionMessage(e)) + ) + } else if (identical(os_type, "unix")) { + sysname = Sys.info()[["sysname"]] + clip_cmd = if (identical(sysname, "Darwin")) { + "pbpaste" + } else if (identical(sysname, "Linux")) { + if (nzchar(Sys.which("wl-paste"))) "wl-paste --no-newline" + else if (nzchar(Sys.which("xclip"))) "xclip -o -selection clipboard" + else if (nzchar(Sys.which("xsel"))) "xsel --clipboard --output" + else stopf("Clipboard reading on Linux requires 'xclip', 'xsel', or 'wl-paste' to be installed and on PATH.") + } + clip = tryCatch(system(clip_cmd, intern = TRUE), + error = function(e) stopf("Reading clipboard failed: %s", conditionMessage(e)) + ) + status = attr(clip, "status") + if (!is.null(status) && status != 0L) { + stopf("Reading clipboard failed (exit %d). Ensure '%s' is working.", status, clip_cmd) + } + } else { + warning("Clipboard reading is not supported on this platform.", call. = FALSE) + } + if (!length(clip) || !any(nzchar(trimws(clip)))) { + stopf("Clipboard is empty.") + } + input = paste(clip, collapse = "\n") } else if (length(grep(' ', input, fixed=TRUE)) && !file.exists(gsub("^file://", "", input))) { # file name or path containing spaces is not a command. file.exists() doesn't understand file:// (#7550) cmd = input if (input_has_vars && getOption("datatable.fread.input.cmd.message", TRUE)) { diff --git a/inst/tests/tests.Rraw b/inst/tests/tests.Rraw index 508bf6aa0..41236886e 100644 --- a/inst/tests/tests.Rraw +++ b/inst/tests/tests.Rraw @@ -21520,3 +21520,48 @@ test(2365.1, melt(df_melt, id.vars=1:2), melt(dt_melt, id.vars=1:2)) df_dcast = data.frame(a = c("x", "y"), b = 1:2, v = 3:4) dt_dcast = data.table(a = c("x", "y"), b = 1:2, v = 3:4) test(2365.2, dcast(df_dcast, a ~ b, value.var = "v"), dcast(dt_dcast, a ~ b, value.var = "v")) + +# Test fread clipboard input on Windows (issue #1292) +if (.Platform$OS.type == "windows") local({ + temp = c("a\tb", "1\t2") + utils::writeClipboard(temp) + on.exit(utils::writeClipboard(""), add = TRUE) + test(2366, fread("clipboard-128"), data.table(a = 1L, b = 2L)) +}) + +# Test fread clipboard input on macOS (issue #1292) +if (.Platform$OS.type == "unix" && identical(Sys.info()[["sysname"]], "Darwin")) local({ + if (nzchar(Sys.which("pbcopy"))) { + con = pipe("pbcopy", "w") + writeLines(c("a\tb", "1\t2"), con) + close(con) + on.exit({ + cl = pipe("pbcopy", "w") + writeLines("", cl) + close(cl) + }, add=TRUE) + test(2366.1, fread("clipboard"), data.table(a=1L, b=2L)) + } +}) + +# Test fread clipboard input on Linux via xclip/xsel/wl-paste (issue #1292) +if (.Platform$OS.type == "unix" && identical(Sys.info()[["sysname"]], "Linux")) local({ + has_wl = nzchar(Sys.which("wl-copy")) && nzchar(Sys.which("wl-paste")) + has_xclip = nzchar(Sys.which("xclip")) + has_xsel = nzchar(Sys.which("xsel")) + writer = if (has_wl) "wl-copy" + else if (has_xclip) "xclip -i -selection clipboard" + else if (has_xsel) "xsel --clipboard --input" + else "" + if (nzchar(writer)) { + con = pipe(writer, "w") + writeLines(c("a\tb", "1\t2"), con) + close(con) + on.exit({ + cl = pipe(writer, "w") + writeLines("", cl) + close(cl) + }, add=TRUE) + test(2366.2, fread("clipboard"), data.table(a=1L, b=2L)) + } +}) \ No newline at end of file