Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
docs: add example to filter DOWN servers from a CSV (#236)
Signed-off-by: shreyaabaranwal <shreyabaranwal229@gmail.com>
  • Loading branch information
shreyaabaranwal committed Jun 19, 2026
commit c30c52d253ff4fc3bdd0055087e8496ffb9b3c76
2 changes: 2 additions & 0 deletions examples/count_errors.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
//go:build ignore

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm wondering if we actually need this. I mean, it's absolutely correct in the sense that this file isn't part of the script package. On the other hand, one thing about example programs is that people will copy and paste them exactly as is—that's what they're for, after all. Including this line would stop their program working, which might be very puzzling for beginners.

Does it do us any harm to omit the build tag in examples?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good question — I tried it, and removing the build tag does break things: with both count_errors.go and filter_csv.go declaring func main() in the same examples/ directory, go build ./... and go vet ./... fail with "main redeclared". go run file.go handles one file fine, but a directory-wide build compiles them as one package, so the tag was working around exactly that.

To keep them copy-paste-able without the tag, my instinct is to give each example its own subdirectory (examples/count_errors/main.go, examples/filter_csv/main.go) — scales cleanly and go build ./... stays happy. But it's your call on the layout. Which would you prefer?

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, we're working against the grain of Go a little bit here, aren't we? It's actually quite awkward to include a copypastable program in a Go module that doesn't cause a main conflict.

Maybe what we should do instead is use Go's built-in example mechanism. For example (sorry), we could add this to script_test.go:

func Example_count_lines() {
	count, err := script.File("examples/app.log").Match("ERROR").CountLines()
	if err != nil {
		panic(err)
	}
	fmt.Printf("Number of ERROR lines: %d\n", count)
}

func Example_filter_csv() {
	// Column splits on whitespace, so turn the comma into a space first,
	// then take the first field (the server name).
	_, err := script.File("examples/servers.csv").Match("DOWN").Replace(",", " ").Column(1).Stdout()
	if err != nil {
		panic(err)
	}
}

As you probably know, because the function names start with Example, they'll be built as part of the autogenerated documentation, and because they don't include the names of any other identifier, they'll be treated as package-level examples:

Screenshot 2026-06-29 at 13 25 11

We can put the data files in the testdata folder—I don't think the public pkgsite instance will let you read these, but that's okay. People can copy and paste the code and it'll work.


// This program reads a log file, filters only the lines containing "ERROR", and prints the count of those lines.
// It uses the github.com/bitfield/script library to handle the file reading, matching, and counting.
//
Expand Down
31 changes: 31 additions & 0 deletions examples/filter_csv.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
//go:build ignore

// This program reads a CSV file containing server names and their status (comma separated),
// filters only the lines where the status is "DOWN", and prints the name of each such server (first column).
// It uses the github.com/bitfield/script library to handle the file reading, matching, replacing, and filtering.
//
// Equivalent shell command:
// grep DOWN servers.csv | cut -d, -f1

package main

import (
"fmt"
"os"

"github.com/bitfield/script"
)

func main() {
// Check if examples/servers.csv exists, otherwise fallback to servers.csv.
csvFile := "examples/servers.csv"
if _, err := os.Stat(csvFile); os.IsNotExist(err) {
csvFile = "servers.csv"
}

_, err := script.File(csvFile).Match("DOWN").Replace(",", " ").Column(1).Stdout()
if err != nil {
fmt.Fprintf(os.Stderr, "Error reading CSV file: %v\n", err)
os.Exit(1)
}
}
5 changes: 5 additions & 0 deletions examples/servers.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
server1,UP
server2,DOWN
server3,DOWN
server4,UP
server5,DOWN