diff --git a/README.md b/README.md index 79f3ec4..33a566f 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ To start using GJSON, install Go and run `go get`: $ go get -u github.com/tidwall/gjson ``` -This will retrieve the library. +This will retrieve the library. [You can also use GJSON as a CLI.](#use-as-cli) ## Get a value Get searches json for the specified path. A path is in dot syntax, such as "name.last" or "age". This function expects that the json is well-formed. Bad json will not panic, but it may return back unexpected results. When the value is found it's returned immediately. @@ -409,6 +409,66 @@ widget.text.size *These benchmarks were run on a MacBook Pro 15" 2.8 GHz Intel Core i7 using Go 1.8.* +## Use as CLI + +### Installation + +```sh +$ go get -u github.com/tidwall/gjson/... +$ which gjson +$GOPATH/bin/gjson +``` + +### Usage + +View the help dialogue with the `-help` flag. + +```sh +$ gjson -help +usage: gjson [options] [path ...] + -in string + read JSON data from this file instead of stdin + -incl-path + include paths in output + -out string + write result to this file instead of stdout + -version + print version and exit +``` + +By default, GJSON reads from stdin and writes to stdout. Use the `-in` / `-out` flags to specify files to read to / write from. Note that GJSON **overwrites** the contents of `-out` (and creates the file if it doesn't exist yet). + +GJSON expects one or more paths as command line arguments. A path with escaped characters should be wrapped in quotes. + +### Examples + +```sh +$ echo '{"name":{"first":"Janet","last":"Prichard"},"age":47}' | gjson name.last +Prichard +``` + +The following examples use the JSON data from [this section](#path-syntax). + +```sh +$ gjson name.last < data.json +Anderson +``` + +```sh +$ gjson name.last age "fav\.movie" < data.json > out +$ cat out +Anderson +37 +Deer Hunter +``` + +```sh +$ gjson -in data.json -out out -include-path children children.# +$ cat out +"children" : ["Sara","Alex","Jack"] +"children.#" : 3 +``` + ## Contact Josh Baker [@tidwall](http://twitter.com/tidwall) diff --git a/cmd/gjson/gjson.go b/cmd/gjson/gjson.go new file mode 100644 index 0000000..2b698fd --- /dev/null +++ b/cmd/gjson/gjson.go @@ -0,0 +1,98 @@ +package main + +import ( + "errors" + "flag" + "fmt" + "io/ioutil" + "log" + "os" + + "github.com/tidwall/gjson" +) + +const version = "0.0.1" + +var options struct { + in string + out string + includePath bool + version bool +} + +func readInput() ([]byte, error) { + if len(options.in) > 0 { + return ioutil.ReadFile(options.in) + } + + // Check that stdin is not empty. + stat, err := os.Stdin.Stat() + if err != nil { + return []byte{}, err + } + if (stat.Mode() & os.ModeCharDevice) != 0 { + return []byte{}, errors.New("expected JSON data from stdin (run gjson -help)") + } + + return ioutil.ReadAll(os.Stdin) +} + +func printOutput(results []gjson.Result) error { + var ( + out *os.File + err error + ) + + if len(options.out) > 0 { + out, err = os.Create(options.out) + if err != nil { + return err + } + defer out.Close() + } else { + out = os.Stdout + } + + for i, result := range results { + if options.includePath { + fmt.Fprintf(out, "\"%s\" : ", flag.Args()[i]) + } + fmt.Fprintln(out, result) + } + + return nil +} + +func main() { + flag.Usage = func() { + fmt.Fprintf(os.Stderr, "usage: %s [options] [path ...]\n", os.Args[0]) + flag.PrintDefaults() + } + + flag.StringVar(&options.in, "in", "", "read JSON data from this file instead of stdin") + flag.StringVar(&options.out, "out", "", "write result to this file instead of stdout") + flag.BoolVar(&options.includePath, "include-path", false, "include paths in output") + flag.BoolVar(&options.version, "version", false, "print version and exit") + flag.Parse() + + if options.version { + fmt.Printf("gjson v%v\n", version) + os.Exit(0) + } + + if len(flag.Args()) == 0 { + flag.Usage() + os.Exit(1) + } + + bytes, err := readInput() + if err != nil { + log.Fatal(err.Error()) + } + + results := gjson.GetMany(string(bytes), flag.Args()...) + err = printOutput(results) + if err != nil { + log.Fatal(err.Error()) + } +}