Tôi đã viết hai hướng dẫn ứng dụng CLI để xây dựnggololcatvàgocowsay. Trong cả hai, tôi đã sử dụngfortune
như là bộ tạo đầu vào.
Trong bài viết này, tôi sẽ hoàn thành bộ ba đường ống vớigofortune
.
Những gì làvận may, Đầu tiên?Như Wikipedia nói, Fortune là một chương trình đơn giản hiển thị một thông điệp giả từ một cơ sở dữ liệu các câu danh ngôn.
Về cơ bản, một trình tạo báo giá ngẫu nhiên.
Nó có một lịch sử rất lâu đời kể từ phiên bản Unix 7 (1979). Nó vẫn đang tiếp tục mạnh mẽ. Nhiều bản phân phối Linux cài đặt sẵn nó và trên OSX, bạn có thể cài đặt nó bằng cách sử dụngbrew install fortune
.
Trên một số hệ thống, nó được sử dụng như một lời chào hoặc thông điệp chia tay khi sử dụng shell.
Wikipedia cũng nói
Nhiều người chọn cách đưa tài sản vàoCowayđể thêm tính hài hước vào hộp thoại.
Chính là tôi! Ngoại trừ tôi sử dụnggocowsay
chỉ huy.
Đủ với phần giới thiệu,hãy xây dựng một bản sao tài sản với Go.
Đây là bảng phân tích về những gì chương trình của chúng tôi sẽ thực hiện.
Vị trí thư mục vận may phụ thuộc vào hệ thống và phân phối, là một lá cờ xây dựng. Tôi có thể mã hóa nó hoặc sử dụng một biến môi trường nhưng như một bài tập, tôi sẽ làm một việc bẩn thỉu và yêu cầufortune
trực tiếp, bằng cách thực hiện nó với-f
cờ, kết quả đầu ra:
Dòng đầu tiên của đầu ra chứa đường dẫn của thư mục vận may.
package main
import (
“fmt”
“os/exec”
)
func main() {
out, err := exec.Command(“fortune”, “-f”).CombinedOutput()
if err != nil {
panic(err)
}
<span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Println</span>(string(<span style="color:#a6e22e">out</span>))
}
Đoạn mã này sao chép đầu ra chính xác như tôi đã nhận. Có vẻ nhưfortune -f
ghi đầu ra vào stderr, đó là lý do tại sao tôi sử dụngCombinedOutput
, để có được cả haistdout
vàstderr
.
Nhưng, tôi chỉ muốn dòng đầu tiên. Làm thế nào để làm nó? Điều này in tất cả đầu ra củastderr
từng dòng:
package main
import (
“bufio”
“fmt”
“os/exec”
)
func main() {
fortuneCommand := exec.Command(“fortune”, “-f”)
pipe, err := fortuneCommand.StderrPipe()
if err != nil {
panic(err)
}
for outputStream.Scan() {
fmt.Println(outputStream.Text())
}
}
Để chỉ lấy dòng đầu tiên, tôi xóa vòng lặp for và chỉ quét dòng đầu tiên:
package main
import (
“bufio”
“fmt”
“os/exec”
)
func main() {
fortuneCommand := exec.Command(“fortune”, “-f”)
pipe, err := fortuneCommand.StderrPipe()
if err != nil {
panic(err)
}
fortuneCommand.Start()
outputStream := bufio.NewScanner(pipe)
outputStream.Scan()
fmt.Println(outputStream.Text())
}
Bây giờ chúng ta hãy chọn dòng đó và trích xuất đường dẫn.
Trên hệ thống của tôi, dòng đầu tiên của đầu ra là100.00% /usr/local/Cellar/fortune/9708/share/games/fortunes
. Hãy tạo một chuỗi con bắt đầu từ lần xuất hiện đầu tiên của/
char:
line := outputStream.Text()
path := line[strings.Index(line, "/"):]
Bây giờ tôi có con đường của vận may. Tôi có thể lập chỉ mục các tệp được tìm thấy trong đó. Có.dat
tệp nhị phân và tệp văn bản thuần túy. Tôi sẽ loại bỏ các tệp nhị phân vàoff/
thư mục hoàn toàn chứa vận may tấn công.
Đầu tiên hãy lập chỉ mục các tệp. tôi sử dụngpath/filepath
gói hàngWalk
phương pháp lặp lại cây tệp bắt đầu từroot
. Tôi sử dụng nó thay vìioutil.ReadDir()
bởi vì chúng ta có thể đã lồng vào nhau các thư mục về vận may. bên trongWalkFunc
visit
Tôi loại bỏ các tệp .dat bằng cách sử dụngfilepath.Ext()
, Tôi loại bỏ các tệp thư mục (ví dụ:/off
, nhưng không phải là các tệp trong các thư mục con) và tất cả các tình huống tấn công, được đặt ở vị trí thuận tiện dưới/off
và tôi in giá trị của từng tệp còn lại.
func visit(path string, f os.FileInfo, err error) error {
if strings.Contains(path, "/off/") {
return nil
}
if filepath.Ext(path) == ".dat" {
return nil
}
if f.IsDir() {
return nil
}
files = append(files, path)
return nil
}
func main() {
fortuneCommand := exec.Command(“fortune”, “-f”)
pipe, err := fortuneCommand.StderrPipe()
if err != nil {
panic(err)
}
fortuneCommand.Start()
outputStream := bufio.NewScanner(pipe)
outputStream.Scan()
line := outputStream.Text()
root := line[strings.Index(line, “/”):]
<span style="color:#a6e22e">err</span> = <span style="color:#a6e22e">filepath</span>.<span style="color:#a6e22e">Walk</span>(<span style="color:#a6e22e">root</span>, <span style="color:#a6e22e">visit</span>)
<span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
panic(<span style="color:#a6e22e">err</span>)
}
}
Hãy đặt những giá trị đó trong một lát cắt để sau này tôi có thể chọn một giá trị ngẫu nhiên: Tôi xác định mộtfiles
phần chuỗi và tôi thêm vào phần đó trongvisit()
chức năng. Ở cuối củamain()
Tôi in số lượng tệp tôi nhận được.
package main
import (
“bufio”
“log”
“os”
“os/exec”
“path/filepath”
“strings”
)
var files []string
func visit(path string, f os.FileInfo, err error) error {
if err != nil {
log.Fatal(err)
}
if strings.Contains(path, “/off/”) {
return nil
}
if filepath.Ext(path) == “.dat” {
return nil
}
if f.IsDir() {
return nil
}
files = append(files, path)
return nil
}
func main() {
fortuneCommand := exec.Command(“fortune”, “-f”)
pipe, err := fortuneCommand.StderrPipe()
if err != nil {
panic(err)
}
fortuneCommand.Start()
outputStream := bufio.NewScanner(pipe)
outputStream.Scan()
line := outputStream.Text()
root := line[strings.Index(line, “/”):]
<span style="color:#a6e22e">err</span> = <span style="color:#a6e22e">filepath</span>.<span style="color:#a6e22e">Walk</span>(<span style="color:#a6e22e">root</span>, <span style="color:#a6e22e">visit</span>)
<span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
panic(<span style="color:#a6e22e">err</span>)
}
println(len(<span style="color:#a6e22e">files</span>))
}
Bây giờ tôi sử dụngĐi đến trình tạo số ngẫu nhiênchức năng để chọn một mục ngẫu nhiên từ mảng:
// Returns an int >= min, < max
func randomInt(min, max int) int {
return min + rand.Intn(max-min)
}
func main() {
<span style="color:#75715e">//...
rand.Seed(time.Now().UnixNano())
i := randomInt(1, len(files))
randomFile := files[i]
println(randomFile)
}
Chương trình của chúng tôi hiện in một tên tệp tài sản ngẫu nhiên trên mỗi lần chạy.
Những gì tôi nhớ bây giờ là quét các vận may trong một tệp, và in ra một tệp ngẫu nhiên. Trong mỗi tệp, các dấu ngoặc kép được phân tách bằng dấu%
ngồi trên một dòng của riêng mình. Tôi có thể dễ dàng phát hiện mẫu này và quét mọi trích dẫn trong một mảng:
file, err := os.Open(randomFile)
if err != nil {
panic(err)
}
defer file.Close()
b, err := ioutil.ReadAll(file)
if err != nil {
panic(err)
}
quotes := string(b)
quotesSlice := strings.Split(quotes, “%”)
j := randomInt(1, len(quotesSlice))
fmt.Print(quotesSlice[j])
Điều này không thực sự hiệu quả, vì tôi đang quét toàn bộ tệp tài sản trong một lát và sau đó tôi chọn một mục ngẫu nhiên, nhưng nó hoạt động:
Vì vậy, đây là phiên bản cuối cùng củafortune
nhân bản. Nó nhớ rất nhiều bản gốcfortune
lệnh, nhưng đó là một sự khởi đầu.
package main
import (
“bufio”
“fmt”
“io/ioutil”
“log”
“math/rand”
“os”
“os/exec”
“path/filepath”
“strings”
“time”
)
var files []string
// Returns an int >= min, < max
func randomInt(min, max int) int {
return min + rand.Intn(max-min)
}
func visit(path string, f os.FileInfo, err error) error {
if err != nil {
log.Fatal(err)
}
if strings.Contains(path, “/off/”) {
return nil
}
if filepath.Ext(path) == “.dat” {
return nil
}
if f.IsDir() {
return nil
}
files = append(files, path)
return nil
}
func main() {
fortuneCommand := exec.Command(“fortune”, “-f”)
pipe, err := fortuneCommand.StderrPipe()
if err != nil {
panic(err)
}
fortuneCommand.Start()
outputStream := bufio.NewScanner(pipe)
outputStream.Scan()
line := outputStream.Text()
root := line[strings.Index(line, “/”):]
<span style="color:#a6e22e">err</span> = <span style="color:#a6e22e">filepath</span>.<span style="color:#a6e22e">Walk</span>(<span style="color:#a6e22e">root</span>, <span style="color:#a6e22e">visit</span>)
<span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
panic(<span style="color:#a6e22e">err</span>)
}
<span style="color:#a6e22e">rand</span>.<span style="color:#a6e22e">Seed</span>(<span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Now</span>().<span style="color:#a6e22e">UnixNano</span>())
<span style="color:#a6e22e">i</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">randomInt</span>(<span style="color:#ae81ff">1</span>, len(<span style="color:#a6e22e">files</span>))
<span style="color:#a6e22e">randomFile</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">files</span>[<span style="color:#a6e22e">i</span>]
<span style="color:#a6e22e">file</span>, <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">os</span>.<span style="color:#a6e22e">Open</span>(<span style="color:#a6e22e">randomFile</span>)
<span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
panic(<span style="color:#a6e22e">err</span>)
}
<span style="color:#66d9ef">defer</span> <span style="color:#a6e22e">file</span>.<span style="color:#a6e22e">Close</span>()
<span style="color:#a6e22e">b</span>, <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">ioutil</span>.<span style="color:#a6e22e">ReadAll</span>(<span style="color:#a6e22e">file</span>)
<span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
panic(<span style="color:#a6e22e">err</span>)
}
<span style="color:#a6e22e">quotes</span> <span style="color:#f92672">:=</span> string(<span style="color:#a6e22e">b</span>)
<span style="color:#a6e22e">quotesSlice</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">strings</span>.<span style="color:#a6e22e">Split</span>(<span style="color:#a6e22e">quotes</span>, <span style="color:#e6db74">"%"</span>)
<span style="color:#a6e22e">j</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">randomInt</span>(<span style="color:#ae81ff">1</span>, len(<span style="color:#a6e22e">quotesSlice</span>))
<span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Print</span>(<span style="color:#a6e22e">quotesSlice</span>[<span style="color:#a6e22e">j</span>])
}
Kết thúc, tôi di chuyểnvisit
như một đối số hàm nội tuyến củafilepath.Walk
và di chuyểnfiles
trở thành một biến cục bộ bên trongmain()
thay vì một biến tệp chung:
package main
import (
“bufio”
“fmt”
“io/ioutil”
“log”
“math/rand”
“os”
“os/exec”
“path/filepath”
“strings”
“time”
)
// Returns an int >= min, < max
func randomInt(min, max int) int {
return min + rand.Intn(max-min)
}
func main() {
var files []string
<span style="color:#a6e22e">fortuneCommand</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">exec</span>.<span style="color:#a6e22e">Command</span>(<span style="color:#e6db74">"fortune"</span>, <span style="color:#e6db74">"-f"</span>)
<span style="color:#a6e22e">pipe</span>, <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">fortuneCommand</span>.<span style="color:#a6e22e">StderrPipe</span>()
<span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
panic(<span style="color:#a6e22e">err</span>)
}
<span style="color:#a6e22e">fortuneCommand</span>.<span style="color:#a6e22e">Start</span>()
<span style="color:#a6e22e">outputStream</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">bufio</span>.<span style="color:#a6e22e">NewScanner</span>(<span style="color:#a6e22e">pipe</span>)
<span style="color:#a6e22e">outputStream</span>.<span style="color:#a6e22e">Scan</span>()
<span style="color:#a6e22e">line</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">outputStream</span>.<span style="color:#a6e22e">Text</span>()
<span style="color:#a6e22e">root</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">line</span>[<span style="color:#a6e22e">strings</span>.<span style="color:#a6e22e">Index</span>(<span style="color:#a6e22e">line</span>, <span style="color:#e6db74">"/"</span>):]
<span style="color:#a6e22e">err</span> = <span style="color:#a6e22e">filepath</span>.<span style="color:#a6e22e">Walk</span>(<span style="color:#a6e22e">root</span>, <span style="color:#66d9ef">func</span>(<span style="color:#a6e22e">path</span> <span style="color:#66d9ef">string</span>, <span style="color:#a6e22e">f</span> <span style="color:#a6e22e">os</span>.<span style="color:#a6e22e">FileInfo</span>, <span style="color:#a6e22e">err</span> <span style="color:#66d9ef">error</span>) <span style="color:#66d9ef">error</span> {
<span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
<span style="color:#a6e22e">log</span>.<span style="color:#a6e22e">Fatal</span>(<span style="color:#a6e22e">err</span>)
}
<span style="color:#66d9ef">if</span> <span style="color:#a6e22e">strings</span>.<span style="color:#a6e22e">Contains</span>(<span style="color:#a6e22e">path</span>, <span style="color:#e6db74">"/off/"</span>) {
<span style="color:#66d9ef">return</span> <span style="color:#66d9ef">nil</span>
}
<span style="color:#66d9ef">if</span> <span style="color:#a6e22e">filepath</span>.<span style="color:#a6e22e">Ext</span>(<span style="color:#a6e22e">path</span>) <span style="color:#f92672">==</span> <span style="color:#e6db74">".dat"</span> {
<span style="color:#66d9ef">return</span> <span style="color:#66d9ef">nil</span>
}
<span style="color:#66d9ef">if</span> <span style="color:#a6e22e">f</span>.<span style="color:#a6e22e">IsDir</span>() {
<span style="color:#66d9ef">return</span> <span style="color:#66d9ef">nil</span>
}
<span style="color:#a6e22e">files</span> = append(<span style="color:#a6e22e">files</span>, <span style="color:#a6e22e">path</span>)
<span style="color:#66d9ef">return</span> <span style="color:#66d9ef">nil</span>
})
<span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
panic(<span style="color:#a6e22e">err</span>)
}
<span style="color:#a6e22e">rand</span>.<span style="color:#a6e22e">Seed</span>(<span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Now</span>().<span style="color:#a6e22e">UnixNano</span>())
<span style="color:#a6e22e">i</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">randomInt</span>(<span style="color:#ae81ff">1</span>, len(<span style="color:#a6e22e">files</span>))
<span style="color:#a6e22e">randomFile</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">files</span>[<span style="color:#a6e22e">i</span>]
<span style="color:#a6e22e">file</span>, <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">os</span>.<span style="color:#a6e22e">Open</span>(<span style="color:#a6e22e">randomFile</span>)
<span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
panic(<span style="color:#a6e22e">err</span>)
}
<span style="color:#66d9ef">defer</span> <span style="color:#a6e22e">file</span>.<span style="color:#a6e22e">Close</span>()
<span style="color:#a6e22e">b</span>, <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">ioutil</span>.<span style="color:#a6e22e">ReadAll</span>(<span style="color:#a6e22e">file</span>)
<span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
panic(<span style="color:#a6e22e">err</span>)
}
<span style="color:#a6e22e">quotes</span> <span style="color:#f92672">:=</span> string(<span style="color:#a6e22e">b</span>)
<span style="color:#a6e22e">quotesSlice</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">strings</span>.<span style="color:#a6e22e">Split</span>(<span style="color:#a6e22e">quotes</span>, <span style="color:#e6db74">"%"</span>)
<span style="color:#a6e22e">j</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">randomInt</span>(<span style="color:#ae81ff">1</span>, len(<span style="color:#a6e22e">quotesSlice</span>))
<span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Print</span>(<span style="color:#a6e22e">quotesSlice</span>[<span style="color:#a6e22e">j</span>])
}
Tôi có thể ngay bây giờgo build; go install
và bộ bagofortune
gocowsay
vàgololcat
đã hoàn thành:
Các hướng dẫn về go khác:
- Sử dụng NGINX Reverse Proxy để phục vụ các dịch vụ Go
- Tạo bản sao của cấu trúc trong Go
- Khái niệm cơ bản về Máy chủ Web Go
- Sắp xếp loại bản đồ trong Go
- Tóm lại về con trỏ
- Giải thích về Thẻ Go
- Định dạng Ngày và Giờ
- Xử lý JSON với Go
- Đi các chức năng đa dạng
- Go Strings Cheat Sheet
- Giải thích về giao diện trống rỗng
- Gỡ lỗi Go với VS Code và Delve
- Named Go trả về các tham số
- Tạo chuỗi và số ngẫu nhiên trong Go
- Cấu trúc hệ thống tệp của một dự án Go
- Thuật toán tìm kiếm nhị phân được triển khai trong Go
- Sử dụng cờ dòng lệnh khi di chuyển
- GOPATH giải thích
- Xây dựng ứng dụng Dòng lệnh với Go: lolcat
- Xây dựng lệnh CLI với Go: coway
- Sử dụng Shell Pipes with Go
- Hướng dẫn về CLI: nhân bản may mắn
- Liệt kê các tệp trong một thư mục với Go
- Sử dụng Go để lấy danh sách các kho từ GitHub
- Tiếp tục, nối một đoạn chuỗi vào một tệp
- Chuyển đổi một chuỗi thành một lát byte
- Hình dung các đóng góp Git địa phương của bạn với Go
- Bắt đầu với Go cấu hình CPU và bộ nhớ
- Giải quyết lỗi "không hỗ trợ lập chỉ mục" trong chương trình Go
- Đo thời gian thực thi trong chương trình cờ vây
- Xây dựng Trình thu thập thông tin web với Go để phát hiện các tiêu đề trùng lặp
- Thực hiện các phương pháp hay nhất: Con trỏ hoặc bộ thu giá trị?
- Thực hiện các phương pháp hay nhất: Bạn nên sử dụng một phương pháp hay một hàm?
- Đi cấu trúc dữ liệu: Đặt
- Go Maps Cheat Sheet
- Tạo triển khai cho các loại chung trong Go
- Truy cập cấu trúc dữ liệu: Từ điển
- Truy cập cấu trúc dữ liệu: Bảng băm
- Triển khai sự kiện Người nghe trong Đi qua các Kênh
- Truy cập cấu trúc dữ liệu: Ngăn xếp
- Truy cập cấu trúc dữ liệu: Hàng đợi
- Đi cấu trúc dữ liệu: Cây tìm kiếm nhị phân
- Đi cấu trúc dữ liệu: Đồ thị
- Truy cập cấu trúc dữ liệu: Danh sách được liên kết
- Hướng dẫn đầy đủ về cấu trúc dữ liệu Go
- So sánh các giá trị Go
- Go có hướng đối tượng không?
- Làm việc với Cơ sở dữ liệu SQL trong Go
- Sử dụng các biến môi trường trong Go
- Xem hướng dẫn: REST API được hỗ trợ bởi PostgreSQL
- Bật CORS trên Máy chủ Web Go
- Triển khai ứng dụng Go trong Docker Container
- Tại sao Go là một ngôn ngữ mạnh mẽ để học với tư cách là một nhà phát triển PHP
- Đi, xóa ký tự dòng mới io.Reader.ReadString
- Bắt đầu, cách xem các thay đổi và xây dựng lại chương trình của bạn
- Đi, đếm tháng kể từ một ngày
- Truy cập thông số HTTP POST trong Go