Sink does not release file
Asked Answered
T

4

34

I know that the sink() function can be used to divert R output into a file, e.g.

sink('sink-closing.txt')
cat('Hello world!')
sink()

Is there a simple command to close all outstanding sinks?

Below, I elaborate on my question.

Suppose that my R-script opens a sink() in an R-script, but there is an error in the R-script which occurs before the script closes the sink(). I may run the R-script multiple times, trying to fix the error. Finally, I want to close all the sinks and print to the console. How do I do so?

Finally, in the interest of concreteness, I provide a MWE to illustrate the problem I face.

First, I write an R-script sink-closing.R which has an error in it.

sink('sink-closing.txt')

foo <- function() {
  cat(sprintf('Hello world! My name is %s\n', 
              a.variable.that.does.not.exist))
}

foo()

sink()

Next, I source the R-script multiple times, say 3 times by mistake as I try to find and fix the bug.

> source('~/Dropbox/cookbook/r-cookbook/sink-closing.R')
Error in sprintf("Hello world! My name is %s\n", a.variable.that.does.not.exist) : 
  object 'a.variable.that.does.not.exist' not found

Now, suppose that I am debugging the R-script and want to print to the console. I can call sink() multiple times to close the earlier sinks. If I call it 3 times, then I can finally print to the console as before. But how do I know how many sinks I need to close?

Trumpet answered 10/9, 2013 at 23:45 Comment(1)
Can you use sink.number?Wessels
S
42

You can use sink.number() to tell you how many diversions are already set and then call sink that many times. Putting it into a function you could have this

sink.reset <- function(){
    for(i in seq_len(sink.number())){
        sink(NULL)
    }
}
Singlebreasted answered 11/9, 2013 at 0:1 Comment(1)
Another WIN for reading all the documentation in the help file! :-)Giff
C
49
closeAllConnections()   # .........................

I'm getting upvotes for this as time goes along but Simon.S.A and others are better.

Crumple answered 11/9, 2013 at 3:39 Comment(5)
... now why wouldn't they link to that or mention it in the sink help file?Singlebreasted
They didn't exactly hide the fact that it was a type of connection. And there is a link to the connection page. So it ended up being a two-hop link to ?showConnections.Crumple
@Singlebreasted -- Probably because closeAllConnections is a pretty crude tool to use for this purpose, as it will close any other connections that are open -- not just the currently open 'sinks'.Masson
@JoshO'Brien It's a good point - but I don't think it would hurt if it was at least mentioned in the documentation for sink.Singlebreasted
And for me this has the added benefit of also closing the processes spawned and not terminated by running a foreach loop in RevolutionR on Windows! Double whammy, thanks :-)Pooi
S
42

You can use sink.number() to tell you how many diversions are already set and then call sink that many times. Putting it into a function you could have this

sink.reset <- function(){
    for(i in seq_len(sink.number())){
        sink(NULL)
    }
}
Singlebreasted answered 11/9, 2013 at 0:1 Comment(1)
Another WIN for reading all the documentation in the help file! :-)Giff
V
9

Based on @mnel's comment:

sinkall <- function() {
  i <- sink.number()
  while (i > 0) {
    sink()
    i <- i - 1
  }
}

Should close all open sinks.

You may also encounter this problem when dealing with devices and plots, where the number of open devices isn't reported anywhere. For a more general case you could use this:

stopWhenError <- function(FUN) {
  tryCatch({
    while(TRUE) {
      FUN()
    }
  }, warning = function(w) {
    print("All finished!")
  }, error = function(e) {
    print("All finished!")
  })
}

stopWhenError(sink) # for sink.
stopWhenError(dev.off) # close all open plotting devices.

EDIT: sink throws a warning not an error so I've modified the code so that it won't run forever, whoops!

Vainglorious answered 11/9, 2013 at 0:3 Comment(0)
A
6

Avoid the problem entirely

This problem can be avoided through the use of the 'on.exit' function:

sink("output.txt")
on.exit(sink())
my_function_that_might_error()

Explanation

The most common time I experience this is when an error occurs preventing a sink from closing. For example, the following will leave an open sink after execution.

sink("output.txt")
my_function_that_will_error()
sink()

This can be avoided using on.exit(sink()). This will close the sink "when the current function exits (either naturally or as the result of an error)" (documentation here).

But you do have to change the order:

sink("output.txt")
on.exit(sink())
my_function_that_might_error()

So we create the sink, tell R to close it when it exits, and then execute the code that might error. This will close the sink regardless of whether the code errors or not.


Limitations

Note that the behaviour of on.exit may not work as expected when running interactively. If the line on.exit(sink()) is run at the console, it triggers immediately as all of the current commands have concluded.

Hence, this approach requires either sourcing a script or the sink being created and closed within some R function/process.

See the documentation for on.exit for more information.

Ambrosane answered 11/11, 2021 at 22:37 Comment(1)
Excellent advice. This comment should be getting more upvotes.Crumple

© 2022 - 2024 — McMap. All rights reserved.