Source only part of a file
Using the right tool for the job …
As discussed in the comments, the real solution is to use an IDE that allows sourcing specific parts of a file. There are many existing solutions:
For Vim, there’s Nvim-R.
For Emacs, there’s ESS.
And of course there’s the excellent stand-alone RStudio IDE.
As a special point of note, all of the above solutions work both locally and on a server (accessed via an SSH connection, say). R can even be run on an HPC cluster — it can still communicate with the IDEs if set up properly.
… or … not.
If, for whatever reason, none of the solutions above work, here’s a small module[gist] that can do the job. I generally don’t recommend using it, though.1
#' (Re-)source parts of a file
#'
#' \code{rs} loads, parses and executes parts of a file as if entered into the R
#' console directly (but without implicit echoing).
#'
#' @param filename character string of the filename to read from. If missing,
#' use the last-read filename.
#' @param from first line to parse.
#' @param to last line to parse.
#' @return the value of the last evaluated expression in the source file.
#'
#' @details If both \code{from} and \code{to} are missing, the default is to
#' read the whole file.
rs = local({
last_file = NULL
function (filename, from, to = if (missing(from)) -1 else from) {
if (missing(filename)) filename = last_file
stopifnot(! is.null(filename))
stopifnot(is.character(filename))
force(to)
if (missing(from)) from = 1
source_lines = scan(filename, what = character(), sep = '\n',
skip = from - 1, n = to - from + 1,
encoding = 'UTF-8', quiet = TRUE)
result = withVisible(eval.parent(parse(text = source_lines)))
last_file <<- filename # Only save filename once successfully sourced.
if (result$visible) result$value else invisible(result$value)
}
})
Usage example:
# Source the whole file:
rs('some_file.r')
# Re-soure everything (same file):
rs()
# Re-source just the fifth line:
rs(from = 5)
# Re-source lines 5–10
rs(from = 5, to = 10)
# Re-source everything up until line 7:
rs(to = 7)
1 Funny story: I recently found myself on a cluster with a messed-up configuration that made it impossible to install the required software, but desperately needing to debug an R workflow due to a looming deadline. I literally had no choice but to copy and paste lines of R code into the console manually. This is a situation in which the above might come in handy. And yes, that actually happened.
How to source only part of a JavaScript file?
I think splitting the file into multiple files is the best approach to this, that way you'll benefit from the caching mechanism.
With that said, you can achieve what you asked for in the question by requesting part of the file and then injecting the response text into a script
element and appending that to the document:
let xhr = new XMLHttpRequest();
xhr.open('GET', 'url/of/the/js/file');
xhr.setRequestHeader('Range', 'bytes=10-40'); // set the range of the request
xhr.onload = function () { // when the request finishes
let scriptEl = document.createElement("script"); // create a <script> element
scriptEl.type = "text/javascript";
scriptEl.textContent = xhr.responseText; // set its content to the response text which is the snippet of code you requested (please take a look at this other SO answer https://stackoverflow.com/a/6433770)
document.body.appendChild(scriptEl); // append that element to the body, the code will then be executed
};
// probably a good idea to handle xhr.onerror as well in case something goes wrong
xhr.send();
Only source functions in a .R file
This works without using regex. It's also probably less computationally efficient than regex solutions. It creates a new environment, sources the entire file, then returns only the functions back to the global environment.
SourceFunctions<-function(file) {
MyEnv<-new.env()
source(file=file,local=MyEnv)
list2env(Filter(f=is.function,x=as.list(MyEnv)),
envir=parent.env(environment()))
}
R: source() and path to source files
If you are distributing a script to colleagues, you should really not be writing a script that sources other scripts. What if you want to rename or move functions.R
in the future? What if you need to modify a function in functions.R
, but wrapper.R
relies on the older version of that function? It's a flimsy solution that will cause headache. I would recommend either of the following instead.
Put everything needed into a single, self-contained script and distribute that.
If you really want to separate code into different files, write a package. Might sound like overkill, but packages can actually be very simple and lightweight. In the simplest form a package is just a directory with a
DESCRIPTION
andNAMESPACE
file along with anR/
directory. Hadley breaks this down nicely: https://r-pkgs.org/whole-game.html.
Commit only part of a file's changes in Git
You can use:
git add --patch <filename>
or for short:
git add -p <filename>
Git will break down your file into what it thinks are sensible "hunks" (portions of the file). It will then prompt you with this question:
Stage this hunk [y,n,q,a,d,/,j,J,g,s,e,?]?
Here is a description of each option:
- y stage this hunk for the next commit
- n do not stage this hunk for the next commit
- q quit; do not stage this hunk or any of the remaining hunks
- a stage this hunk and all later hunks in the file
- d do not stage this hunk or any of the later hunks in the file
- g select a hunk to go to
- / search for a hunk matching the given regex
- j leave this hunk undecided, see next undecided hunk
- J leave this hunk undecided, see next hunk
- k leave this hunk undecided, see previous undecided hunk
- K leave this hunk undecided, see previous hunk
- s split the current hunk into smaller hunks
- e manually edit the current hunk
- You can then edit the hunk manually by replacing
+
/-
by#
(thanks veksen)
- You can then edit the hunk manually by replacing
- ? print hunk help
If the file is not in the repository yet, you can first do git add -N <filename>
. Afterwards you can go on with git add -p <filename>
.
Afterwards, you can use:
git diff --staged
to check that you staged the correct changesgit reset -p
to unstage mistakenly added hunksgit commit -v
to view your commit while you edit the commit message.
Note this is far different than the git format-patch
command, whose purpose is to parse commit data into a .patch
files.
Reference for future: Git Tools - Interactive Staging
Rstudio difference between run and source
Run
and source
have subtly different meanings. According to the RStudio documentation,
The difference between running lines from a selection and invoking
Source is that when running a selection all lines are inserted
directly into the console whereas for Source the file is saved to a
temporary location and then sourced into the console from there
(thereby creating less clutter in the console).
Something to be aware of, is that sourcing functions in files makes them available for scripts to use. What does this mean? Imagine you are trying to troubleshoot a function that is called from a script. You need to source
the file containing the function, to make the changes available in the function be used when that line in the script is then run
.
A further aspect of this is that you can source
functions from your scripts. I use this code to automatically source all of the functions in a directory, which makes it easy to run a long script with a single run
:
# source our functions
code.dir <- "c:\temp"
code.files = dir(code.dir, pattern = "[.r]")
for (file in code.files){
source(file = file.path(code.dir,file))
}
Find the source file containing R function definition
Digging into the srcref
attribute of one of the loaded functions appears to work, if you go deep enough ...
source("tmp/tmpsrc.R")
str(util.add)
## function (a, b)
## - attr(*, "srcref")=Class 'srcref' atomic [1:8] 1 13 1 31 13 31 1 1
## .. ..- attr(*, "srcfile")=Classes 'srcfilecopy', 'srcfile' <environment: 0x8fffb18>
srcfile <- attr(attr(util.add,"srcref"),"srcfile")
ls(srcfile)
## [1] "Enc" "filename" "fixedNewlines" "isFile"
## [5] "lines" "parseData" "timestamp" "wd"
srcfile$filename
## [1] "tmp/tmpsrc.R"
How to get the part of a file after the first line that matches a regular expression
The following will print the line matching TERMINATE
till the end of the file:
sed -n -e '/TERMINATE/,$p'
Explained: -n
disables default behavior of sed
of printing each line after executing its script on it, -e
indicated a script to sed
, /TERMINATE/,$
is an address (line) range selection meaning the first line matching the TERMINATE
regular expression (like grep) to the end of the file ($
), and p
is the print command which prints the current line.
This will print from the line that follows the line matching TERMINATE
till the end of the file:
(from AFTER the matching line to EOF, NOT including the matching line)
sed -e '1,/TERMINATE/d'
Explained: 1,/TERMINATE/
is an address (line) range selection meaning the first line for the input to the 1st line matching the TERMINATE
regular expression, and d
is the delete command which delete the current line and skip to the next line. As sed
default behavior is to print the lines, it will print the lines after TERMINATE
to the end of input.
If you want the lines before TERMINATE
:
sed -e '/TERMINATE/,$d'
And if you want both lines before and after TERMINATE
in two different files in a single pass:
sed -e '1,/TERMINATE/w before
/TERMINATE/,$w after' file
The before and after files will contain the line with terminate, so to process each you need to use:
head -n -1 before
tail -n +2 after
IF you do not want to hard code the filenames in the sed script, you can:
before=before.txt
after=after.txt
sed -e "1,/TERMINATE/w $before
/TERMINATE/,\$w $after" file
But then you have to escape the $
meaning the last line so the shell will not try to expand the $w
variable (note that we now use double quotes around the script instead of single quotes).
I forgot to tell that the new line is important after the filenames in the script so that sed knows that the filenames end.
How would you replace the hardcoded TERMINATE
by a variable?
You would make a variable for the matching text and then do it the same way as the previous example:
matchtext=TERMINATE
before=before.txt
after=after.txt
sed -e "1,/$matchtext/w $before
/$matchtext/,\$w $after" file
to use a variable for the matching text with the previous examples:
## Print the line containing the matching text, till the end of the file:
## (from the matching line to EOF, including the matching line)
matchtext=TERMINATE
sed -n -e "/$matchtext/,\$p"
## Print from the line that follows the line containing the
## matching text, till the end of the file:
## (from AFTER the matching line to EOF, NOT including the matching line)
matchtext=TERMINATE
sed -e "1,/$matchtext/d"
## Print all the lines before the line containing the matching text:
## (from line-1 to BEFORE the matching line, NOT including the matching line)
matchtext=TERMINATE
sed -e "/$matchtext/,\$d"
The important points about replacing text with variables in these cases are:
- Variables (
$variablename
) enclosed insingle quotes
['
] won't "expand" but variables insidedouble quotes
["
] will. So, you have to change all thesingle quotes
todouble quotes
if they contain text you want to replace with a variable. - The
sed
ranges also contain a$
and are immediately followed by a letter like:$p
,$d
,$w
. They will also look like variables to be expanded, so you have to escape those$
characters with a backslash [\
] like:\$p
,\$d
,\$w
.
Related Topics
Add Moving Average Plot to Time Series Plot in R
Display Only Months in Daterangeinput or Dateinput for a Shiny App [R Programming]
How to Merge Two Columns in R with a Specific Symbol
How to Automatically Include All 2-Way Interactions in a Glm Model in R
How to Include Interactive Input in Script to Be Run from the Command Line
How to Install R Package from Private Repo Using Devtools Install_Github
How Does One Change the Levels of a Factor Column in a Data.Table
Can't Load X11 in R After Os X Yosemite Upgrade
Changing Shapes Used for Scale_Shape() in Ggplot2
Vectorize a Product Calculation Which Depends on Previous Elements
What Algorithm I Need to Find N-Grams
What Does the Diff() Function in R Do
Using 'Rvest' to Extract Links
The Right Way to Plot Multiple Y Values as Separate Lines with Ggplot2