DEV Community

Yatharth Kelkar
Yatharth Kelkar

Posted on

# Why I Used Bash, C and C# to Build One Linux Tool

Using three programming languages to build a small Linux system-information tool sounds unnecessary.

And it is.

But that was the point.

I wanted to build a project that forced me to understand how different layers of software can work together.

The result was LinuxGuard — a small Linux system-intelligence project built using Bash, C, and C#/.NET.

The idea

LinuxGuard collects information from a Linux machine and turns it into a readable Markdown report.

The architecture looks like this:

                    LinuxGuard
                        │
                        ▼
                Bash orchestrator
                   /          \
                  /            \
                 ▼              ▼
                C              C#
                 │              │
                 ▼              │
          Linux system data     │
                 │              │
                 ▼              │
          system.keyvalue ──────┘
                        │
                        ▼
                Markdown report
Enter fullscreen mode Exit fullscreen mode

Each language has a specific responsibility.

  • Bash handles orchestration and automation.
  • C collects low-level Linux system information.
  • C#/.NET processes the collected information and generates the report.

The goal wasn't to create three unrelated programs.

The goal was to make them behave like one system.

Why Bash?

Bash is responsible for running the pipeline.

It handles things such as:

  • Dependency checks
  • Build commands
  • Execution order
  • File paths
  • User-facing status messages

The entire project can therefore be started with:

./scripts/linuxguard.sh
Enter fullscreen mode Exit fullscreen mode

The script performs the major stages of the workflow:

[1/3] Building C probe...
[2/3] Collecting local system snapshot...
[3/3] Building C# report generator...
Enter fullscreen mode Exit fullscreen mode

This makes Bash the orchestrator of the system.

Why C?

The C component is the low-level system probe.

It collects information from the local Linux system and writes the results into a simple format.

For example:

kernel=...
architecture=...
hostname=...
logical_cpus=...
memory_mb=...
Enter fullscreen mode Exit fullscreen mode

The C program can also be compiled independently:

gcc -Wall -Wextra -Wpedantic \
    src/c/sysprobe.c \
    -o bin/sysprobe
Enter fullscreen mode Exit fullscreen mode

I deliberately used strict compiler warnings:

  • -Wall
  • -Wextra
  • -Wpedantic

Part of the point of the project was to become more comfortable with systems programming and the Linux environment.

Why C#?

The C# component has a completely different job.

It takes the collected data and turns it into something useful for a human: a Markdown system report.

system.keyvalue
       │
       ▼
 ReportBuilder
       │
       ▼
system-report.md
Enter fullscreen mode Exit fullscreen mode

This lets me use C#/.NET for structured parsing, file handling, and report generation.

The important part: the data contract

One of the design decisions I like most about LinuxGuard is the interface between C and C#.

Instead of tightly coupling the two programs together, the C program writes:

system.keyvalue
Enter fullscreen mode Exit fullscreen mode

and the C# program reads it.

Conceptually:

C
 │
 │ writes
 ▼
system.keyvalue
 │
 │ reads
 ▼
C#
Enter fullscreen mode Exit fullscreen mode

That simple file becomes a data contract.

As long as the contract remains compatible, the C and C# components can evolve independently.

That means I can improve the system-information collector without having to completely redesign the report generator.

What happens when LinuxGuard runs?

The complete pipeline looks like this:

             User
              │
              ▼
       linuxguard.sh
              │
       ┌──────┴──────┐
       ▼             ▼
   GCC build      .NET build
       │             │
       ▼             │
    sysprobe         │
       │             │
       ▼             │
Linux system data    │
       │             │
       ▼             │
system.keyvalue ─────┘
              │
              ▼
        ReportBuilder
              │
              ▼
       system-report.md
Enter fullscreen mode Exit fullscreen mode

The final output is a readable Markdown report.

Why not just use one language?

A production tool this small probably wouldn't need three languages.

A single language could handle the entire workflow.

But LinuxGuard isn't primarily a production utility.

It's a systems-learning project.

I wanted to understand:

  • Bash orchestration
  • C compilation
  • Linux system interfaces
  • C data handling
  • C#/.NET
  • File I/O
  • Data contracts
  • Build pipelines
  • Git and GitHub
  • Cross-language architecture

Using three languages made the boundaries between those concepts much more visible.

Security considerations

LinuxGuard is intended to remain a local system-information and learning tool.

That also means system information needs to be handled carefully.

A system-information tool can easily become problematic if it starts collecting things it shouldn't.

I deliberately don't want the project automatically collecting or publishing sensitive information such as:

  • Passwords
  • Private keys
  • Authentication tokens
  • Browser credentials
  • Personal files
  • Private network secrets

If I add more collectors in the future, security and privacy need to remain part of the design.

What's next?

There are a lot of directions I could take LinuxGuard.

Potential future features include:

  • CPU information
  • GPU information
  • Disk usage
  • Mounted filesystems
  • Running processes
  • Network interfaces
  • IP addresses
  • Listening ports
  • SSH configuration checks
  • Firewall status
  • Permission checks
  • Outdated-package detection
  • HTML reports
  • JSON output
  • Terminal dashboards
  • Security scoring
  • Automated tests
  • GitHub Actions
  • Cross-distribution support

For example, I'd eventually like the command-line interface to support something like:

./scripts/linuxguard.sh --security
Enter fullscreen mode Exit fullscreen mode

The interesting part isn't simply making LinuxGuard bigger.

It's making the architecture better as it grows.

What I learned

The biggest lesson from this project wasn't a particular Bash command, C function, or C# class.

It was learning how to give different components clear responsibilities.

Bash answers:

How do I run everything?

C answers:

How do I collect information from Linux?

C# answers:

How do I turn that information into something useful?

Once those boundaries were clear, the project became much easier to reason about.

That's what I wanted LinuxGuard to teach me.

Not just programming languages.

Software architecture.

Top comments (0)