# 从命令行读取参数
## 12.4.1 os包
os包中有一个string类型的切片变量`os.Args`，其用来处理一些基本的命令行参数，它在程序启动后读取命令行输入的参数。来看下面的打招呼程序：

示例 12.11 [os_args.go](examples/chapter_12/os_args.go)：
```go
// os_args.go
package main

import (
	"fmt"
	"os"
	"strings"
)

func main() {
	who := "Alice "
	if len(os.Args) > 1 {
		who += strings.Join(os.Args[1:], " ")
	}
	fmt.Println("Good Morning", who)
}
```

我们在IDE或编辑器中直接运行这个程序输出：`Good Morning Alice`

我们在命令行运行`os_args or ./os_args`会得到同样的结果。

但是我们在命令行加入参数，像这样：`os_args John Bill Marc Luke`，将得到这样的输出：`Good Morning Alice John Bill Marc Luke`

这个命令行参数会放置在切片`os.Args[]`中（以空格分隔），从索引1开始（`os.Args[0]`放的是程序本身的名字，在本例中是`os_args`）。函数`strings.Join`函数用来连接这些参数，以空格作为间隔。

**练习 12.5**：[hello_who.go](exercises/chapter_12/hello_who.go)
写一个"Hello World"的变种程序：把人的名字作为程序命令行执行的一个参数，比如： `hello_who Evan Michael Laura` 那么会输出`Hello Evan Michael Laura`!

## 12.4.2 flag包
flag包有一个扩展功能用来解析命令行选项。但是通常被用来替换基本常量，例如，在某些情况下我们希望在命令行给常量一些不一样的值。（参看19章的项目）

在flag包中一个Flag被定义成一个含有如下字段的结构体：

```go
type Flag struct {
	Name     string // name as it appears on command line
	Usage    string // help message
	Value    Value  // value as set
	DefValue string // default value (as text); for usage message
}
```

下面的程序`echo.go`模拟了Unix的echo功能：

```go
package main

import (
	"flag" // command line option parser
	"os"
)

var NewLine = flag.Bool("n", false, "print newline") // echo -n flag, of type *bool

const (
	Space   = " "
	Newline = "\n"
)

func main() {
	flag.PrintDefaults()
	flag.Parse() // Scans the arg list and sets up flags
	var s string = ""
	for i := 0; i < flag.NArg(); i++ {
		if i > 0 {
			s += " "
			if *NewLine { // -n is parsed, flag becomes true
				s += Newline
			}
		}
		s += flag.Arg(i)
	}
	os.Stdout.WriteString(s)
}
```

`flat.Parse()`扫描参数列表（或者常量列表）并设置flag, `flag.Arg(i)`表示第i个参数。`Parse()`之后所有`flag.Arg(i)`全部可用，`flag.Arg(0)`就是第一个真实的flag，而不是像`os.Args(o)`放置程序的名字。

`flag.Narg()`返回参数的数量。解析后flag或常量就可用了。`flag.Bool()`定义了一个默认值是`false`的flag：当在命令行出现了第一个参数（这里是"n"），flag被设置成'true'（NewLine是`*bool`类型）。如果`*NewLine`表示对flag解引用，所以当值是`true`时将添加一个newline。

`flag.PrintDefaults()`打印flag的使用帮助信息，本例中打印的是：

```go
	-n=false: print newline
```

`flag.VisitAll(fn func(*Flag))`是另一个有用的功能：按照字典顺序遍历flag，并且对每个标签调用fn(参考15.8章的例子)

当在命令行（Windows）中执行：`echo.exe A B C`，将输出：`A B C`；执行`echo.exe -n A B C`，将输出：

```go
A
B
C
```

每个字符的输出都新起一行，每次都在输出的数据前面打印使用帮助信息：`-n=false: print newline`

对于`flag.Bool`你可以设置布尔型flag来测试你的代码，例如定义一个flag `processedFlag`:

```go
	var processedFlag = flag.Bool(“proc”, false, “nothing processed yet”)
```

在后面用如下代码来测试：

```go
	if *processedFlag { // found flag -proc
		r = process()
	}
```

要给flag定义其它类型，可以使用`flag.Int()`，`flag.Float64`，`flag.String()`

在15.8章你将找到一个完成的例子。



## 链接

- [目录](directory.md)
- 上一节：[文件拷贝](12.3.md)
- 下一节：[用buffer读取文件](12.5.md)


