How to create stream pipe output in shell

Question

My print.swift is like:

// print.swift
let result = Task {
    for index in 0..<3 {
        try await Task.sleep(nanoseconds: 1 * 1000 * 1000 * 1000)
        print("Compile \(index)")
    }
    return 0
}
_ = try await result.value

If I use: swift print.swift, then the output will be:

# wait 1s
Compile 1
# wait 1s
Compile 2
# wait 1s
Compile 3

But if I use swift print.swift | cat, then the output will be:

# wait 3s
Compile 1
Compile 2
Compile 3

Why? What's the difference?

Things I've try

  1. Changing print.swift to this doesn't change the result:
// print.swift
import Foundation
for index in 0..<3 {
    usleep(1000000)
    print("Compile \(index)")
}
  1. using print.sh or print.sh | cat has the same output which met my expectation:
# print.sh
for index in {0..2}; do
	sleep 1
	echo "Compile $index"
done

outputs:

# wait 1s
Compile 1
# wait 1s
Compile 2
# wait 1s
Compile 3
  1. compile print.swift to print.swift.o using swiftc doesn't change the result.

This is standard Unix-y stuff. I wrote up an explanation here. Please read it through and write back here if you have follow-up questions.

Share and Enjoy

Quinn “The Eskimo!” @ DTS @ Apple

3 Likes

Thanks for the explanation! I agree it's sort of some buffer related stuff.

So can I think when compiling swift, the default behavior of print will be _IOFBF instead of _IOLBF or _IONBF? If so, can I change this behavior in swift?