diff --git a/Library/Homebrew/test/utils/popen_spec.rb b/Library/Homebrew/test/utils/popen_spec.rb
index c3f6027cf7241570b0d5181a3b75439e2a08be15..dcce91d8b46b0fc1e1fb0d4aedc68397d4beb231 100644
--- a/Library/Homebrew/test/utils/popen_spec.rb
+++ b/Library/Homebrew/test/utils/popen_spec.rb
@@ -26,11 +26,49 @@ describe Utils do
   end
 
   describe "::popen_write" do
-    it "with supports writing to a command's standard input" do
+    let(:foo) { mktmpdir/"foo" }
+
+    before { foo.write "Foo\n" }
+
+    it "supports writing to a command's standard input" do
       subject.popen_write("grep", "-q", "success") do |pipe|
-        pipe.write("success\n")
+        pipe.write "success\n"
+      end
+      expect($CHILD_STATUS).to be_a_success
+    end
+
+    it "returns the command's standard output before writing" do
+      child_stdout = subject.popen_write("cat", foo, "-") do |pipe|
+        pipe.write "Bar\n"
+      end
+      expect($CHILD_STATUS).to be_a_success
+      expect(child_stdout).to eq <<~EOS
+        Foo
+        Bar
+      EOS
+    end
+
+    it "returns the command's standard output after writing" do
+      child_stdout = subject.popen_write("cat", "-", foo) do |pipe|
+        pipe.write "Bar\n"
+      end
+      expect($CHILD_STATUS).to be_a_success
+      expect(child_stdout).to eq <<~EOS
+        Bar
+        Foo
+      EOS
+    end
+
+    it "supports interleaved writing between two reads" do
+      child_stdout = subject.popen_write("cat", foo, "-", foo) do |pipe|
+        pipe.write "Bar\n"
       end
       expect($CHILD_STATUS).to be_a_success
+      expect(child_stdout).to eq <<~EOS
+        Foo
+        Bar
+        Foo
+      EOS
     end
   end
 end
diff --git a/Library/Homebrew/utils/popen.rb b/Library/Homebrew/utils/popen.rb
index f9d189219c1d2957961aa8488f5701a769f30e15..eac4823c64c90354d4cc57223a3d62437ab80b7d 100644
--- a/Library/Homebrew/utils/popen.rb
+++ b/Library/Homebrew/utils/popen.rb
@@ -1,6 +1,9 @@
 # frozen_string_literal: true
 
 module Utils
+  IO_DEFAULT_BUFFER_SIZE = 4096
+  private_constant :IO_DEFAULT_BUFFER_SIZE
+
   def self.popen_read(*args, **options, &block)
     popen(args, "rb", options, &block)
   end
@@ -12,8 +15,25 @@ module Utils
     raise ErrorDuringExecution.new(args, status: $CHILD_STATUS, output: [[:stdout, output]])
   end
 
-  def self.popen_write(*args, **options, &block)
-    popen(args, "wb", options, &block)
+  def self.popen_write(*args, **options)
+    popen(args, "w+b", options) do |pipe|
+      output = ""
+
+      # Before we yield to the block, capture as much output as we can
+      loop do
+        output += pipe.read_nonblock(IO_DEFAULT_BUFFER_SIZE)
+      rescue IO::WaitReadable, EOFError
+        break
+      end
+
+      yield pipe
+      pipe.close_write
+      IO.select([pipe])
+
+      # Capture the rest of the output
+      output += pipe.read
+      output.freeze
+    end
   end
 
   def self.safe_popen_write(*args, **options, &block)