无库获取进程标准流及自制编程语言控制台实现技术问询
Hey there! Let's break down your questions step by step—since you're building your own programming language, understanding how standard streams work under the hood is key for adding solid console app support.
At the core, standard streams (stdin, stdout, stderr) are just OS-managed I/O channels. To access them without dependencies, you need to call platform-specific system APIs directly—no third-party libraries required. Here's how it works on the two major platforms:
Unix-like Systems (Linux, macOS, BSD)
On these systems, standard streams are represented by file descriptors:
0= stdin (input)1= stdout (output)2= stderr (error)
You can use raw system calls to read/write these descriptors:
// Example: Read from stdin without libraries #include <unistd.h> // Contains raw syscall declarations char buffer[1024]; ssize_t bytes_read = read(0, buffer, sizeof(buffer)); // Read from stdin (fd 0) write(1, buffer, bytes_read); // Echo back to stdout (fd 1)
For capturing/redirecting streams (e.g., capturing a child process's output), use pipe() to create a pipe, then dup2() to replace the child's stdout/stderr with the pipe's write end. The parent process can then read from the pipe's read end.
Windows
Windows uses handles instead of file descriptors. You first retrieve the standard stream handles via GetStdHandle(), then use I/O functions to interact with them:
#include <windows.h> char buffer[1024]; DWORD bytes_read; HANDLE h_stdin = GetStdHandle(STD_INPUT_HANDLE); ReadConsoleA(h_stdin, buffer, sizeof(buffer), &bytes_read, NULL); // Read from console HANDLE h_stdout = GetStdHandle(STD_OUTPUT_HANDLE); WriteConsoleA(h_stdout, buffer, bytes_read, &bytes_read, NULL); // Write to console
To capture child process streams, use CreatePipe() to create anonymous pipes, then configure the child's STARTUPINFO structure to use these pipes instead of the default console handles.
Let's look at how C# and C++ handle standard streams—this will give you concrete references for your own language.
C# Console Class
The Console class in .NET is a cross-platform wrapper that abstracts away OS-specific details:
- On Windows, methods like
Console.WriteLine()ultimately callWriteConsoleW()(the wide-character Windows API) to write to the console. For redirected streams (e.g., output to a file), it usesWriteFile()instead. - On Unix-like systems, it uses the
write()system call directly on file descriptor1(stdout). - The class also handles encoding conversion (since .NET strings are UTF-16, while OS streams may use UTF-8 or other encodings) and buffering (by default, it uses line buffering for console output).
Key point: The Console class doesn't reinvent the wheel—it's a thin, user-friendly layer over raw OS APIs.
C++ cout
When you compile C++ code using cout, here's what happens:
std::coutis an instance ofstd::ostreamthat's pre-bound to the standard output stream (file descriptor1on Unix, Windows console handle on Windows).- The
<<operator (e.g.,cout << "Hello") translates to calls toostream::operator<<(), which eventually writes bytes to the underlying OS stream viawrite()(Unix) or Windows I/O functions. - By default,
coutuses line buffering—meaning output is only flushed to the console when a newline (\n) is encountered, or when you explicitly callstd::flushorstd::endl. - The standard library (libstdc++, libc++) provides the implementation, but at its core, it's just wrapping raw system calls—no magic involved.
Here's how to translate these concepts into your language's implementation:
- Abstract the API first: Define simple, user-facing functions like
console.read(),console.write(text), andconsole.error(text). This hides platform complexity from your language's users. - Implement platform-specific backends:
- For Unix-like systems: Use raw
read(),write(), andpipe()syscalls. Handle encoding conversion (e.g., convert UTF-8 bytes from stdin to your language's internal string format, and vice versa for output). - For Windows: Use
GetStdHandle(),ReadConsole(),WriteConsole(), andCreatePipe()for redirection. Pay attention to wide vs. narrow character handling if your language uses Unicode.
- For Unix-like systems: Use raw
- Add stream capture support:
- For capturing the current process's own streams: Allow users to redirect stdout/stderr to a buffer or file by replacing the underlying file descriptor/handle (use
dup2()on Unix,SetStdHandle()on Windows). - For capturing child process streams: Implement a way to spawn processes with redirected stdio pipes, then expose an API to read from those pipes.
- For capturing the current process's own streams: Allow users to redirect stdout/stderr to a buffer or file by replacing the underlying file descriptor/handle (use
- Handle buffering: Decide on a buffering strategy (line-buffered is standard for consoles) and let users flush manually if needed (e.g.,
console.flush()). - Test edge cases: Make sure your implementation works with redirected streams (e.g.,
./your-program > output.txt) and handles errors gracefully (e.g., when stdin is closed).
内容的提问来源于stack exchange,提问作者Rotem Steiner

