-2

Good afternoon,

I'm trying to grab processes id's with my limited knowledge on golang and the below is what I've come up with:

    package main

import (
    "fmt"
    "io/ioutil"
    "log"
    "os"
    "os/exec"
    "strings"
)

func main() {
    cmd := exec.Command("tasklist.exe")
    out, err := cmd.CombinedOutput()
    if err != nil {
        log.Fatal(err)
    }
    f, err := os.Create("data.txt")

    if err != nil {
        log.Fatal(err)
    }

    defer f.Close()

    val := out
    data := []byte(val)

    _, err2 := f.Write(data)

    if err2 != nil {
        log.Fatal(err2)
    }

    val2 := " and red fox\n"
    data2 := []byte(val2)

    var idx int64 = int64(len(data))

    _, err3 := f.WriteAt(data2, idx)

    if err3 != nil {
        log.Fatal(err3)
    }

    fmt.Println("done")
    /* ioutil.ReadFile returns []byte, error */
    explorer, err := ioutil.ReadFile("data.txt")
    /* ... omitted error check..and please add ... */
    /* find index of newline */
    file := string(data)
    line := 0
    /* func Split(s, sep string) []string */
    temp := strings.Split(file, "\n")

    for _, item := range temp {
        fmt.Println("[", line, "]\t", item)
        line++
    }
    fmt.Println(explorer)
}

My Main issue is that i keep running into the same wall where ioutil won't let me assign a value before reading the file.

Is anyone able to help me out here?

Lewis
  • 3
  • 2
  • To Preface, for this example, I want to grab the line with the explorer.exe PID on it from the Tasklist, if there is an easier way to do this please let me know – Lewis Mar 06 '22 at 02:16
  • Try removing `defer f.Close()` and adding `f.Close()` on the line before `fmt.Println("done")`. My initial thought was that you are trying to open data.txt again (with `ioutil.ReadFile()`) while it's already open from the `os.Create()` call. I assume you're on Windows? The example worked as-is for me on linux (my data was "hello world", however), but I maybe file handles work different in your environment (ie Windows doesn't allow you to open and already open file, or doesn't allow a single process to have multiple handles on the same file, etc). – Benny Jobigan Mar 06 '22 at 03:43
  • @BennyJobigan Thank you for your comment, please allow me to confirm a few things. - I am indeed on windows so yes a single process can only have 1 handle to a file at once, the issue I’m having is with having the file already open so closing and reopening this could work. Thank you for your help. – Lewis Mar 06 '22 at 03:57
  • @Lewis Well, my thought was wrong. I ran your example on a windows 10 VM and didn't get any errors... just a bunch of lines of my processes from the for-loop over `temp`. – Benny Jobigan Mar 06 '22 at 04:14

1 Answers1

0

I don't know what's not working in your original example, but a few minutes of experimentation and reading the docs for tasklist.exe gave me something better. The main improvement is letting tasklist.exe do the heavy lifting by giving it args.

package main

import (
    "bytes"
    "encoding/csv"
    "fmt"
    "log"
    "os/exec"
)

func main() {
    // original command:
    // tasklist.exe /nh /fo csv /fi "IMAGENAME eq explorer.exe"
    // /nh = no header
    // /fo csv = format output to csv
    // /fi = filter on query
    //
    // run tasklist.exe /? for more info
    //
    // sample output:
    // "explorer.exe","4860","Console","1","102,240 K"

    // start tasklist with args to do the hard work for us.
    // we want CSV output (easier to parse), and let tasklist search for explorer.exe.
    // obviously you could seach for another task name instead of explorer.exe.
    //
    // the key thing here is that the query ("IMAGENAME ...") doesn't
    // require " when passed as an arg because exec.Command()
    // will apparently handle that for you 
    // (quoting like `"IMAGENAME..."` produced an error from Output() )
    cmd := exec.Command("tasklist.exe", "/nh", "/fo", "csv", "/fi", "IMAGENAME eq explorer.exe")
    out, err := cmd.Output()
    if err != nil {
        log.Fatalln(err)
    }

    // create a csv reader around the output of the above command.
    // then use ReadAll() to split the csv into [][]string.. a slice
    // of "lines" (aka records, rows) where each line is a slice of 
    // csv "columns" (aka fields, entries).
    r := csv.NewReader(bytes.NewReader(out))
    lines, err := r.ReadAll()
    if err != nil {
        log.Fatalln(err)
    }

    // the result may have multiple processes (eg svchost.exe) or none.
    for _, line := range lines {
        // the process id is at index 1 as a string.
        // do whatever you need to do with the result.
        fmt.Println(line[1])
    }
}
Benny Jobigan
  • 5,078
  • 2
  • 31
  • 41