Skip to content

Commit fc0bbdd

Browse files
committed
Create 2025-11-05-understanding-stdin-stdout-and-stderr-in-dotnet.md
1 parent b63496f commit fc0bbdd

1 file changed

Lines changed: 110 additions & 0 deletions

File tree

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
---
2+
layout: post
3+
title: "Understanding stdin, stdout and stderr in .NET"
4+
description: "A practical introduction for .NET developers to standard input, output, and error streams — with examples for the console, pipes, and redirection."
5+
date: 2025-11-05 23:59
6+
author: Robert Muehsig
7+
tags: [Console, .NET, Windows]
8+
language: en
9+
---
10+
11+
{% include JB/setup %}
12+
13+
**Confession time:**
14+
It’s almost embarrassing to admit this — but after years of writing .NET code and using the terminal daily, I recently discovered that you can redirect the *error output* of a program using `2>`.
15+
16+
So I decided to write this post — partly as a note to my future self, and partly for anyone else who’s ever thought:
17+
"Wait… why are there *two* output streams?"
18+
19+
# Overview
20+
21+
When you build console applications or CLI tools in .NET, sooner or later you’ll encounter these basic "input"/"output"-streams:
22+
23+
- **stdin** — standard input (like keyboard, files, or pipes) or in C# `Console.In`, `Console.ReadLine()`
24+
- **stdout** — standard output or in C# `Console.Out`, `Console.WriteLine()`
25+
26+
Besides the "standard output" there is a special output just for errors or warnings:
27+
28+
- **stderr** - Error or diagnostic messages or in C# `Console.Error`, `Console.Error.WriteLine()`
29+
30+
Having a dedicated error stream means your program can separate *data output* (for other programs or files) from *diagnostic messages* (for humans).
31+
32+
# A Simple Example
33+
34+
Let’s build a small C# program that reads from stdin, writes to stdout, and reports problems on stderr.
35+
36+
```csharp
37+
using System;
38+
39+
class Program
40+
{
41+
static void Main()
42+
{
43+
Console.WriteLine("== Start ==");
44+
45+
string? line;
46+
int lineCount = 0;
47+
48+
// Read from stdin
49+
while ((line = Console.ReadLine()) != null)
50+
{
51+
lineCount++;
52+
53+
// Normal output
54+
Console.WriteLine($"[{lineCount}] {line}");
55+
56+
// Print errors separately
57+
if (line.Contains("error", StringComparison.OrdinalIgnoreCase))
58+
{
59+
Console.Error.WriteLine($"Line {lineCount}: contains the word 'error'");
60+
}
61+
}
62+
63+
Console.WriteLine("== End ==");
64+
}
65+
}
66+
```
67+
68+
When you run it and type some lines and include the word error, the output will look like this:
69+
70+
```
71+
== Start ==
72+
[1] Hello
73+
[2] error found
74+
Line 2: contains the word 'error'
75+
[3] test
76+
== End ==
77+
```
78+
79+
# Why 2> means stderr
80+
81+
If you run the same app like this with the same input:
82+
83+
```
84+
.\SampleConsoleApp.exe 2> err.txt
85+
```
86+
87+
The `Line 2: contains the word 'error'` will be redirected into the `err.txt`.
88+
89+
# Input as file
90+
91+
Let's say you have a file that should be used as an input, then you could invoke the program like this:
92+
93+
```
94+
.\SampleConsoleApp.exe < input.txt
95+
```
96+
97+
# Input with pipes
98+
99+
You can also use the `|` operator to use this as input stream:
100+
101+
```
102+
"error" | .\SampleConsoleApp.exe 2> err.txt
103+
```
104+
105+
# Final thoughts
106+
107+
The standard streams are one of those Unix-era ideas that aged perfectly.
108+
They make it easy to connect programs like Lego bricks — and .NET gives you full access to them.
109+
110+
Hope this helps!

0 commit comments

Comments
 (0)