J'ai écrit deux didacticiels d'application CLI pour créerGololcatetgocowsay. Dans les deux j'ai utiliséfortune
comme générateur d'entrée.
Dans cet article, je terminerai la triologie des tuyaux avecgofortune
.
Qu'est-ce quefortune, première?Comme le dit Wikipedia, Fortune est un programme simple qui affiche un message pseudo-aléatoire à partir d'une base de données de citations.
Fondamentalement, un générateur de devis aléatoires.
Il a une très longue histoire qui remonte à Unix version 7 (1979). Ça va toujours fort. De nombreuses distributions Linux le préinstallent, et sur OSX, vous pouvez l'installer en utilisantbrew install fortune
.
Sur certains systèmes, il est utilisé comme message d'accueil ou de séparation lors de l'utilisation de shells.
Wikipédia dit aussi
Beaucoup de gens choisissent de faire fortunevachecommande, pour ajouter plus d'humour à la boîte de dialogue.
C'est moi! Sauf que j'utilise mongocowsay
commander.
Assez avec l'intro,construisons un clone de fortune avec Go.
Voici un aperçu de ce que fera notre programme.
L'emplacement du dossier fortunes dépend du système et de la distribution, étant un indicateur de construction. Je pourrais le coder en dur ou utiliser une variable d'environnement, mais comme exercice, je vais faire une chose sale et demanderfortune
directement, en l'exécutant avec le-f
flag, qui génère:
La première ligne de la sortie contient le chemin du dossier fortunes.
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>))
}
Cet extrait de code reproduit la sortie exactement telle que je l'ai obtenue. Il paraît quefortune -f
écrit la sortie sur stderr, c'est pourquoi j'ai utiliséCombinedOutput
, pour obtenir les deuxstdout
etstderr
.
Mais je veux juste la première ligne. Comment faire? Ceci imprime toute la sortie destderr
ligne par ligne:
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())
}
}
Pour obtenir uniquement la première ligne, je supprime la boucle for et scanne simplement la première ligne:
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())
}
Maintenant, choisissons cette ligne et extrayons le chemin.
Sur mon système, la première ligne de la sortie est100.00% /usr/local/Cellar/fortune/9708/share/games/fortunes
. Faisons une sous-chaîne à partir de la première occurrence du/
carboniser:
line := outputStream.Text()
path := line[strings.Index(line, "/"):]
Maintenant, j'ai le chemin des fortunes. Je peux indexer les fichiers qui s'y trouvent. Il y a.dat
fichiers binaires et fichiers texte brut. Je vais supprimer les fichiers binaires, et leoff/
dossier tout à fait, qui contient des fortunes offensives.
Indexons d'abord les fichiers. Je utilise lepath/filepath
paquetWalk
méthode pour parcourir l'arborescence des fichiers à partir deroot
. Je l'utilise au lieu deioutil.ReadDir()
parce que nous pourrions avoir des dossiers imbriqués de fortunes. Dans leWalkFunc
visit
Je supprime les fichiers .dat en utilisantfilepath.Ext()
, Je supprime les fichiers du dossier (par exemple/off
, mais pas les fichiers dans les sous-dossiers) et toutes les fortunes offensives, idéalement situées sous/off
, et j'imprime la valeur de chaque fichier restant.
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>)
}
}
Mettons ces valeurs dans une tranche, afin que je puisse plus tard en choisir une au hasard: je définis unfiles
tranche de chaînes et j'ajoute à cela dans levisit()
fonction. Au bout dumain()
J'imprime le nombre de fichiers que j'ai.
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>))
}
J'utilise maintenant leAller au générateur de nombres aléatoiresfonctionnalité pour choisir un élément aléatoire dans le tableau:
// 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)
}
Notre programme imprime maintenant un nom de fichier fortune aléatoire à chaque exécution.
Ce qui me manque maintenant, c'est de numériser les fortunes dans un fichier et d'en imprimer un au hasard. Dans chaque fichier, les guillemets sont séparés par un%
assis sur une ligne tout seul. Je peux facilement détecter ce modèle et scanner chaque devis dans un tableau:
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])
Ce n'est pas vraiment efficace, car je scanne l'intégralité du fichier fortune dans une tranche, puis je choisis un élément aléatoire, mais cela fonctionne:
Alors, voici la version finale de notre très basiquefortune
cloner. Il manque beaucoup de l'originalfortune
commande, mais c'est un début.
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>])
}
En terminant, je bougevisit
en tant qu'argument de fonction en ligne defilepath.Walk
et bougefiles
être une variable locale à l'intérieurmain()
au lieu d'une variable de fichier globale:
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>])
}
je peux maintenantgo build; go install
et la triologiegofortune
gocowsay
etgololcat
est terminé:
Plus de tutoriels go:
- Utilisation du proxy inverse NGINX pour servir les services Go
- Faire une copie d'une structure dans Go
- Les bases d'un serveur Web Go
- Trier un type de carte dans Go
- Allez les pointeurs en un mot
- Go Tags expliqué
- Aller au formatage de la date et de l'heure
- Traitement JSON avec Go
- Fonctions Go Variadic
- Fiche de triche Go Strings
- L'interface Go Empty expliquée
- Débogage Go avec VS Code et Delve
- Named Go renvoie des paramètres
- Générer des nombres et des chaînes aléatoires dans Go
- Structure du système de fichiers d'un projet Go
- Algorithme de recherche binaire implémenté dans Go
- Utilisation des indicateurs de ligne de commande dans Go
- GOPATH a expliqué
- Créez une application de ligne de commande avec Go: lolcat
- Création d'une commande CLI avec Go: cowsay
- Utilisation de Shell Pipes avec Go
- Tutoriel Go CLI: Clone de fortune
- Lister les fichiers dans un dossier avec Go
- Utilisez Go pour obtenir une liste des référentiels à partir de GitHub
- Allez, ajoutez une tranche de chaînes à un fichier
- Allez, convertissez une chaîne en une tranche d'octets
- Visualisez vos contributions Git locales avec Go
- Premiers pas avec Go CPU et profilage de la mémoire
- Résolution de l'erreur "ne prend pas en charge l'indexation" dans un programme Go
- Mesure du temps d'exécution dans un programme Go
- Création d'un robot d'exploration Web avec Go pour détecter les titres en double
- Go Best Practices: pointeur ou récepteurs de valeur?
- Go Best Practices: Devez-vous utiliser une méthode ou une fonction?
- Go Structures de données: définir
- Aide-mémoire Go Maps
- Générer des implémentations pour les types génériques dans Go
- Go Data Structures: Dictionnaire
- Structures de données Go: table de hachage
- Implémenter des écouteurs d'événements dans Passer par les canaux
- Go Structures de données: pile
- Go Structures de données: file d'attente
- Go Structures de données: arbre de recherche binaire
- Go Structures de données: graphique
- Structures de données Go: liste liée
- Le guide complet des structures de données Go
- Comparaison des valeurs Go
- Est-ce que Go est orienté objet?
- Travailler avec une base de données SQL dans Go
- Utilisation des variables d'environnement dans Go
- Tutoriel Go: API REST soutenue par PostgreSQL
- Activation de CORS sur un serveur Web Go
- Déployer une application Go dans un conteneur Docker
- Pourquoi Go est un langage puissant à apprendre en tant que développeur PHP
- Allez, supprimez le caractère de nouvelle ligne io.Reader.ReadString
- Allez, comment observer les changements et reconstruire votre programme
- Allez, comptez les mois depuis une date
- Accéder aux paramètres HTTP POST dans Go