Go program to find duplicated files in a directory (recursively)
1. declare all 'var' at once

instead of 

var dir string
var workers int


you can do 

var (
    dir string
    workers int
)


or even better, use local var instead of global var irectly in your main() function 

dir := flag.String("dir", ".", "directory to search")
workers := flag.Int("workers", runtime.NumCPU(), "number of workers") 


2. Make sure that arguments are valid

if worker is &lt;= 0, the program will panic. A little check after flag.Parse() could prevent this:

if workers &lt;= 0 {
    fmt.Printf("workers has to be &gt; 0, was %d", workers)
}


3. Improve hash computing:

First, each worker only need a single instance of hash.Hash, as you can call Reset() on it after each file: 

h := sha256.New()
for file := range input {
    ...
    results &lt;- &amp;Result{...}
    h.Reset()
}


Also, the hash of each file could be stored as a string instead of a [32]byte to avoid some operations: 

results &lt;- &amp;Result{
    file:   file,
    sha256: fmt.Sprintf("%x", h.Sum(nil)),
}


4. Always specify the channel direction when you can

From the golang specifications: 


  A channel provides a mechanism for concurrently executing functions
  to communicate by sending and receiving values of a specified element
  type. The value of an uninitialized channel is nil.
  
  ChannelType = ( "chan" | "chan" "&lt;-" | "&lt;-" "chan" ) ElementType .
  
  The optional &lt;- operator specifies the channel direction, send or
  receive. If no direction is given, the channel is bidirectional. A
  channel may be constrained only to send or only to receive by
  conversion or assignment.


Specify the channel direction helps understand what a method is doing 



Here is the new version of the code: 

package main

import (
    "crypto/sha256"
    "flag"
    "fmt"
    "io"
    "os"
    "path/filepath"
    "runtime"
    "sync"
)

type Result struct {
    file   string
    sha256 string
}

func search(dir string, input chan&lt;- string) {
    filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
        if err != nil {
            fmt.Fprintln(os.Stderr, err)
        } else if info.Mode().IsRegular() {
            input &lt;- path
        }
        return nil
    })
    close(input)
}

func startWorker(input &lt;-chan string, results chan&lt;- *Result, wg *sync.WaitGroup) {
    h := sha256.New()
    for file := range input {
        f, err := os.Open("file.txt")
        if err != nil {
            fmt.Fprintln(os.Stderr, err)
            continue
        }
        if _, err := io.Copy(h, f); err != nil {
            fmt.Fprintln(os.Stderr, err)
            f.Close()
            continue
        }
        f.Close()
        results &lt;- &amp;Result{
            file:   file,
            sha256: fmt.Sprintf("%x", h.Sum(nil)),
        }
        h.Reset()
    }
    wg.Done()
}

func run(dir string, workers int) (map[string][]string, error) {

    input := make(chan string)
    go search(dir, input)

    counter := make(map[string][]string)
    results := make(chan *Result)
    go func() {
        for r := range results {
            counter[r.sha256] = append(counter[r.sha256], r.file)
        }
    }()

    var wg sync.WaitGroup
    wg.Add(workers)
    for i := 0; i &lt; workers; i++ {
        go startWorker(input, results, &amp;wg)
    }
    wg.Wait()
    close(results)

    return counter, nil
}

func main() {

    dir := flag.String("dir", ".", "directory to search")
    workers := flag.Int("workers", runtime.NumCPU(), "number of workers")
    flag.Parse()

    if *workers &lt;= 0 {
        fmt.Printf("workers has to be &gt; 0, was %d \n", workers)
        os.Exit(1)
    }
    fmt.Printf("Searching in %s using %d workers...\n", *dir, *workers)

    counter, err := run(*dir, *workers)
    if err != nil {
        fmt.Printf("failed! %v\n", err)
        os.Exit(1)
    }

    for sha, files := range counter {
        if len(files) &gt; 1 {
            fmt.Printf("Found %d duplicates for %v: \n", len(files), sha)
            for _, f := range files {
                fmt.Println("-&gt; ", f)
            }
        }
    }
}




Possible improvements:

Currently, if an error is thrown somewhere in the code, the program does not stop but just write the error to os.Stderr. It may be better to return this error and then call os.Exit(1)