Rake’s sh for example cannot be captured with the normal $stdount = StringIO.new trick, but using some old ActiveSupport code that uses reopen with a Temfile seems to do the trick.
# StringIO does not work with rubies `system` call that `sh` uses under the hood, so using Tempfile + reopen # https://grosser.it/2018/11/23/ruby-capture-stdout-without-stdout def capture_stdout old_stdout = STDOUT.dup Tempfile.open('tee_stdout') do |capture| STDOUT.reopen(capture) STDOUT.sync = true yield capture.rewind capture.read end ensure STDOUT.reopen(old_stdout) end