background

As a Java developer who does a lot of open source software, I often have a small requirement to provide a command-line helper on the terminal, and there are only a few ways to implement this requirement.

  1. Write a batch or Shell (twice for Windows and Linux)
  2. Use a programming language (Golang, Python are good cross-platform choices)

I wrote a similar program in Python a long time ago, but the result was too big. On the other hand, Go is one of my favorite programming languages, and it supports cross-platform, so I chose it.

In this article I will teach you to write a small program can check the local weather, relatively simple, you can learn from this article to make their own favorite gadget.

The full code can be viewed on my Github.

Installation environment

Before we start, we need to prepare the environment for the Go language. You can skip this step if you already have it installed. You can download the latest version of Go here if your network environment is forced to look like this.

You can download the latest release package at Golang China. There are several ways to install the Go language environment, but we chose the simplest one: a standard package installation.

For Mac, download the package file ending in. PKG. For Windows, download the file ending in. Msi.

Note that your operating system architecture is not wrong, Linux source installation is not covered here.

Configuring environment Variables

Learning a lot of programming languages need to configure environment variables, when installing software, in fact, there are some programs quietly help us do this thing, in the previous we installed Go language, let’s learn about Go language environment variables and how to configure.

  • GOROOT: Installation path of Go
  • GOPATH: Tell the Go command and other related tools where to find the Go package installed on your system.

So let’s create a working directory to store the source package we wrote

On Windows, assume D:/go, and on Linux/MacOSX, assume ~/workspace/golang. I’m currently running MacOSX, so I’m going to set the environment variables as follows.

  1. Adding environment variablesexport GOROOT=/usr/local/go
  2. Adding environment variablesexport GOPATH=/Users/biezhi/workspace/golang
  3. Example Modify system environment variablesexport PATH=$PATH:$GOPATH/bin

Test the

# biezhi in ~Go version Go version go1.8.3 Darwin/AMd64Copy the code

Now that you’re done, you need to know the basics of a programming language, or you can’t eat it.

Some differences with the Java language

Here we will mention a few differences that do not cover all the requirements of this article.

Declare variables and constants

In Java

private String name = "biezhi";
public static final String VERSION = "0.2.1";
Copy the code

In the Golang

name : = "biezhi"
const version = "0.2.1"
Copy the code

Ouch, very simple

Classes and objects

In Java

public class Config {
    private String key;

    private String value;

    // getter and setter
}

/ / use
Config config = new Config(a);
config.setKey("name");
config.setValue("biezhi");
Copy the code

In the Golang

type Config struct {
    key string
    value string
}

/ / use
conf : = Config{key: "name". value: "biezhi"}
Copy the code

Ouch, it’s fucking simple again…

Golang does not have the class keyword, but instead introduces type, which is more important in Golang.

Function return value

In Java

Public String getFileName(){return "indescribable.jpg "; }Copy the code

If you want to return more than one value, you need to replace it with a class or Map type.

In the Golang

func getFileNmae(a) (string. error)  {
    return "Can describe.jpg". nil
}
Copy the code

The Go language naturally supports multiple return values (after all, upstart, social)

Closing a stream in Java typically writes code like this

try { 
    in.balabala~
} catch(Exception e) {
    // Handle exceptions
} finally { 
    in.close(a);
}
Copy the code

There is no finally in Go. Try defer

func CopyFile(dstName. srcName string) (written int64. err error) {
    src. err : = os.Open(srcName)
    if err ! = nil {
        return
    }
    dst. err : = os.Create(dstName)
    if err ! = nil {
       return
    }
    written. err = io.Copy(dst. src)
    dst.Close(a)
    src.Close(a)
    return
}
Copy the code

CopyFile method simply realizes the function of copying file content, copy the content of the source file to the target file. At first glance, this doesn’t seem to be a problem, but resources in Golang also need to be freed. If the os.create method is called incorrectly, the following statement will return, and the two open files will not have a chance to be freed. At this point, defer can come in handy.

I just want you to think about what the code for Go looks like. Below began our formal topic, if you still do not understand the foundation can go to see the basic part of the books, materials (can not find the private message I).

Native writing implementation

I won’t come up to teach you how to use a certain library, it is not responsible for, you should be clear in the absence of library era, how people do when there is a more convenient tool for what I reduced, the process you may realize he hasn’t seen API is how to use, in the long term will be flexible use in programming.

There are a few basic packages to know:

  • fmt: format package, which implements formatting I/O similar to C languages printf and scanf
  • encoding/json: native JSON parsing package
  • net/http: Sends an HTTP request
  • flag: provides a series of functional interfaces for parsing command-line arguments
  • io/ioutil: IO processing

So with these packages, let’s write a Hello World and look, my project name is weather-CLI

Bind flag to a variable using the flag.xxxvar () method, which returns a value type such as

package main

import (
   "fmt"
   "flag"
   "os"
)

func main(a) {
   var city string
   flag.StringVar(&city. "c". "Shanghai". "Chinese name of city")
   flag.Parse(a)
   fmt.Println("The city is :". city)
}
Copy the code

Let’s run it

# biezhi in ~/workspace/golang/src/github.com/biezhi/weather-cli
» go build &&./weather-cli The cities are: Shanghai » go build&&/weather-cli -c Beijing City is: BeijingCopy the code

Parsing the parameters is relatively simple, in this demo we add two parameters, the first is the city, the second is to display the day, the code is as follows:

func main(a) {
   var city string
   var day string

   flag.StringVar(&city. "c". "Shanghai". "Chinese name of city")
   flag.StringVar(&day. "d". "Today". "Optional: Today, yesterday, forecast.")
   flag.Parse(a)
}
Copy the code

At this point, the terminal input parameters have been available, so the next time to find an interface to call the weather API, I found this free API interface to call. We need to write a method for the HTTP request.

func Request(url string) (string. error) {
   response. err : = http.Get(url)
   if err ! = nil {
       return "". err
   }
   
   defer response.Body.Close(a)
   body. _ : = ioutil.ReadAll(response.Body)
   return string(body), nil
}
Copy the code

Enter a URL and return the Body of the response as String. We will pass in the city we entered, the default is Shanghai.

var city string
var day string

flag.StringVar(&city. "c". "Shanghai". "Chinese name of city")
flag.StringVar(&day. "d". "Today". "Optional: Today, yesterday, forecast.")
flag.Parse(a)

var body. err = Request(apiUrl + city)
if err ! = nil {
   fmt.Printf("err was %v". err)
   return
}
Copy the code

Then we need to define some type to store JSON, which in the Java language is Class. How this type structure is defined is based on the results returned by the API, which I write separately in the types.go file.

/ / response
type Response struct {
   Status   int    `json:"status"`
   CityName string `json:"city"`
   Data     Data   `json:"data"`
   Date     string `json:"date"`
   Message  string `json:"message"`
   Count    int    `json:"count"`
}

// Response data
type Data struct {
   ShiDu     string `json:"shidu"`
   Quality   string `json:"quality"`
   Ganmao    string `json:"ganmao"`
   Yesterday Day    `json:"yesterday"`
   Forecast  []Day  `json:"forecast"`
}
// Data for a particular day
type Day struct {
   Date    string  `json:"date"`
   Sunrise string  `json:"sunrise"`
   High    string  `json:"high"`
   Low     string  `json:"low"`
   Sunset  string  `json:"sunset"`
   Aqi     float32 `json:"aqi"`
   Fx      string  `json:"fx"`
   Fl      string  `json:"fl"`
   Type    string  `json:"type"`
   Notice  string  `json:"notice"`
}
Copy the code

Once the type is defined, you can parse the JSON from the HTTP request into the defined type.

var r Response
err = json.Unmarshal([]byte(body), &r)

if err ! = nil {
   fmt.Printf("\nError message: %v". err)
}

if r.Status ! = 200 {
   fmt.Printf("Error getting weather API %s". r.Message)
   return
}
Copy the code

The JSON parsing that comes with Go is used here. , finally we will get the data output is Ok.

func Print(day string. r Response) {
   fmt.Println("City.". r.CityName)

   if day = = "Today" {
       fmt.Println("The humidity.". r.Data.ShiDu)
       fmt.Println(Air Quality:. r.Data.Quality)
       fmt.Println("Warm reminder :". r.Data.Ganmao)
   } else if day = = "Yesterday" {
       fmt.Println("Date.". r.Data.Yesterday.Date)
       fmt.Println("Temperature.". r.Data.Yesterday.Low. r.Data.Yesterday.High)
       fmt.Println("Air.". r.Data.Yesterday.Fx. r.Data.Yesterday.Fl)
       fmt.Println("Weather.". r.Data.Yesterday.Type)
       fmt.Println("Warm reminder :". r.Data.Yesterday.Notice)
   } else if day = = "Predict" {
       fmt.Println("= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =")
       for _. item : = range r.Data.Forecast {
           fmt.Println("Date.". item.Date)
           fmt.Println("Temperature.". item.Low. item.High)
           fmt.Println("Air.". item.Fx. item.Fl)
           fmt.Println("Weather.". item.Type)
           fmt.Println("Warm reminder :". item.Notice)
           fmt.Println("= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =")
       }
   } else {
       fmt.Println("Big Bear, do you want to spite me, Fat Tiger? _?")
   }
}
Copy the code

Now that the gadget is working, let’s give it a try

# biezhi in ~/workspace/golang/src/github.com/biezhi/weather-cli
» go build &&/weather-cli City: Shanghai Humidity: 72% Air quality: Good warm tips: very few sensitive people should reduce outdoor activities./weather -CLI -c Beijing City: Beijing Humidity: 78% Air quality: Mild pollution Warm tips: Children, the elderly and patients with heart and respiratory diseases should reduce long-term or high-intensity outdoor exerciseCopy the code

Use third-party libraries

Here we use a popular cli library. How does this guy use it? Create a cli_main.go file

package main

import (
 "fmt"
 "os"
 "github.com/urfave/cli"
)

func main(a) {

   app : = cli.NewApp(a)
   app.Name = "greet"
   app.Usage = "fight the loneliness!"
   app.Action = func(c *cli.Context) error {
      fmt.Println("Hello friend!")
      return nil
   }

   app.Run(os.Args)
}
Copy the code

This is an example from the official website. Run it

» go build cli_main. Go&& ./cli_main
Hello friend!
Copy the code

The implementation of our above small program needs to use flag this function, through the library to achieve the code as follows

func main(a) {

   app : = cli.NewApp(a)
   app.Name = "weather-cli"
   app.Usage = "Weather Forecast Mini-program"

   app.Flags = []cli.Flag{
       cli.StringFlag{
           Name:  "city, c".
           Value: "Shanghai".
           Usage: "Chinese name of city".
       },
       cli.StringFlag{
           Name:  "day, d".
           Value: "Today".
           Usage: "Optional: Today, yesterday, forecast.".
       },
   }

   app.Action = func(c *cli.Context) error {
       city : = c.String("city")
       day : = c.String("day")

       var body. err = Request(apiUrl + city)
       if err ! = nil {
           fmt.Printf("err was %v". err)
           return nil
       }

       var r Response
       err = json.Unmarshal([]byte(body), &r)
       if err ! = nil {
           fmt.Printf("\nError message: %v". err)
           return nil
       }

       if r.Status ! = 200 {
           fmt.Printf("Error getting weather API %s". r.Message)
           return nil
       }
       Print(day. r)
       return nil
   }
   app.Run(os.Args)
}
Copy the code

I just gave you the entire code, just 40 lines or so

Go build -o weather-cli utils.go types.go cli_main.go&&./weather-cli City: Shanghai Humidity: 72% Air quality: Good Tips: Very few sensitive people should reduce outdoor activities » go build -o weather-cli utils. Go types&&/ weather-CLI --city Beijing: Beijing Humidity: 78% Air quality: mild pollution Warm tips: Children, the elderly and patients with heart and respiratory diseases should reduce long-term or high intensity outdoor exerciseCopy the code

Here we learn how to package this little program in binary for use on various platforms, and how to compress the binary package to make it smaller!

Packaging and compression

Programs packaged for each operating system

Linux 64 – bit

GOOS=linux GOARCH=amd64 go build ...
Copy the code

Windows 64 – bit

GOOS=windows GOARCH=amd64 go build ...
Copy the code

MacOSX

GOOS=darwin GOARCH=amd64 go build ...
Copy the code

If you try packaging, the resulting binary is around 7.2m in size, which is a bit large. There are techniques we can use to make it smaller.

First add the compiler parameter -ldFlags

go build -ldflags '-w -s' -o weather-cli utils.go types.go cli_main.go
Copy the code

After execution, we found that the program is only 5.4m, which has become small, but it is still a little too big for us. I will write dozens of lines of code, there is no need to generate such a large size. Next, we use another artifact upX, if you have not installed it, you can download and install it on its official website.

go build -ldflags '-w -s' -o weather-cli utils.go types.go cli_main.go && upx ./weather-cli
Copy the code

Take a look at

'll - la DRWXR xr - x12 biezhi staff  384 Nov  1 18:44 .git
-rw-r--r--  1Biezhi staff Sep 1.1 K3 20:59 LICENSE
-rw-r--r--  1 biezhi staff  710 Nov  1 18:32 README.md
-rwxr-xr-x  1Biezhi staff 5.4 M Nov1 18:41 cli_main
-rw-r--r--  1 biezhi staff  905 Nov  1 18:18 cli_main.go
-rw-r--r--  1 biezhi staff  609 Nov  1 18:19 main.go
-rw-r--r--  1 biezhi staff  808 Nov  1 16:56 types.go
-rw-r--r--  1Nov biezhi staff 1.4 K1 18:19 utils.go
-rwxr-xr-x  1Biezhi staff 2.0 M Nov1 18:44 weather-cli
Copy the code

Only 2.0m, this is almost enough, you drivers learn to be happy ~ want to see more interesting development posture can follow my column “Wangjue’s technology little dark room” or leave a message to me.