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.
- Write a batch or Shell (twice for Windows and Linux)
- 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.
- Adding environment variables
export GOROOT=/usr/local/go
- Adding environment variables
export GOPATH=/Users/biezhi/workspace/golang
- Example Modify system environment variables
export 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 scanfencoding/json
: native JSON parsing packagenet/http
: Sends an HTTP requestflag
: provides a series of functional interfaces for parsing command-line argumentsio/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.