Welcome to Part 4 of my series on my tool for backing up ZFS Snapshots to an external device. In this part, I am discussing how to exec a command and read its output.
To deal with external commands in Go, you use the os/exec
package. The primary pieces of the package that I need for now are exec.Command()
and CombinedOutput()
. exec.Command()
sets up the Command structure with the command and any arguments that I am passing to it.
var listCommand = exec.Command("zfs", "list", "-Hrt", "snapshot", "dpool")
That code creates a variable called listCommand, which is ready to run the command zfs with the arguments list, -Hrt, and snapshot as individual arguments.
var snapList, err = listCommand.CombinedOutput()
That line of code runs the command I previously prepared, puts both its Standard Output and Standard Error in a slice of bytes. If the command exited with an error code other than 0, CombinedOutput sets err to a non-nil value. snapList will have the Standard Error of the executed command, so printing snapList’s contents will be useful for debugging.
var snapScanner = bufio.NewScanner(bytes.NewReader(snapList))
if err != nil {
fmt.Println(listCommand)
fmt.Println("Error trying to list snapshots:", err.Error())
for snapScanner.Scan() {
fmt.Println(snapScanner.Text())
}
}
I will need to use the more complicated IO redirection tools provided in the os/exec
package for the zfs send
and zfs receive
commands. However, for a test run today, I can use a modification of the loop I used to print the output from zfs
if it errored.
for snapScanner.Scan() {
if snapshotLineRegex.MatchString(snapScanner.Text()) {
var temp = strings.SplitN(snapScanner.Text(), "\t", 2)
var snapshot = ParseSnapshot(temp[0])
if snapshot != nil {
fmt.Println("I found snapshot", snapshot.Name(), "at", snapshot.Path())
}
}
}