If you enjoyed this book considering buying a copy
- Buy a copy of the book on Lean Pub
- Buy a copy of the book on Kindle
- Buy a hard copy of the book on Amazon
- Buy the bundle Master Python on Lean Pub
Chapter 10: Command-line Rosetta Stone #
This chapter serves as a guidebook for users coming from another language. The source code can be found here. The same style, hello world command-line tool is written in many languages.
R Hello World #
This step is an example of a hello world command-line tool in R. The source code is here.
#!/usr/bin/env Rscript
#Hello World R command-line tool
#
suppressPackageStartupMessages(library("optparse"))
parser <- OptionParser()
parser <- add_option(parser, c("-c", "--count"), type = "integer",
help = "Number of times to print phrase",
metavar = "number")
parser <- add_option(parser, c("-p", "--phrase"),
help = "Phrase to print")
args <- parse_args(parser)
# Function to Generate Phrases
phrasegen <- function(arguments){
for (count in 1:arguments$count) {
cat(paste(arguments$phrase, "\n"))
}
}
#Run the program
phrasegen(args)
Depends on https://github.com/trevorld/optparse for R
Usage #
$ hello-world git:(master) $ ./hello-world.R --count 5 --phrase "hello world"
hello world
hello world
hello world
hello world
hello world
Bash Hello World #
This step is a hello world Bash example. The source code is here.
#!/bin/bash
#output looks like this:
#
# $hello-world git:(master) $ ./hello-world.sh --count 5 --phrase "hello world"
#hello world
#hello world
#hello world
#hello world
#hello world
#Generate phrase "N" times
phrase_generator() {
for ((i=0; i<$1;i++)); do
echo "$2"
done
}
#Parse Options
while [[ $# -gt 1 ]]
do
key="$1"
case $key in
-c|--count)
COUNT="$2"
shift
;;
-p|--phrase)
PHRASE="$2"
shift
;;
esac
shift
done
#Run program
phrase_generator "${COUNT}" "${PHRASE}"
To lint use make lint
. And now run it:
$ hello-world git:(master) $ ./hello-world.rb --count 5 --phrase "hello world"
hello world
hello world
hello world
hello world
hello world
Environment #
- Installed https://github.com/koalaman/shellcheck in VSCode
- On Homebrew you can install:
brew install shellcheck
You may need to also do:
$ echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.zshenv
$ echo 'eval "$(rbenv init -)"' >> ~/.zshenv
$ echo 'source $HOME/.zshenv' >> ~/.zshrc
$ exec $SHELL
Go Hello World #
The go
source code is here.
package main
import (
"fmt"
"gopkg.in/urfave/cli.v1" // imports as package "cli"
"os"
)
func main() {
app := cli.NewApp()
app.Flags = []cli.Flag{
cli.StringFlag{
Name: "phrase",
Usage: "Print phrase",
},
cli.Int64Flag{
Name: "count",
Usage: "Count to print a phrase",
},
}
app.Action = func(c *cli.Context) error {
sum := 0
for i := 0; i < c.Int("count"); i++ {
sum -= i
fmt.Println(c.String("phrase"))
}
return nil
}
app.Run(os.Args)
}
Running program #
Run make all
Then run program:
$ hello-world git:(master) $ hello-world --phrase "hello world" --count 5
hello world
hello world
hello world
hello world
hello world
Environment #
- Setup on mac with homebrew:
Setting these variables:
export GOPATH="${HOME}/.go"
export GOROOT="$(brew --prefix golang)/libexec"
export PATH="$PATH:${GOPATH}/bin:${GOROOT}/bin"
export GOBIN=$GOPATH/bin
-
Write first program with Go
-
Using cli:
-
Running lint:
make lint
-
Installing dependencies:
make install
Node Hello World #
The source code for the node
examples is here. This project has several components. First, there are the .js
file.
#!/usr/bin/env node
"use strict";
/*
Hello World Commandline Tool
node index.js --phrase "hello world" --count 10
hello world hello world hello world
*/
const program = require('commander');
program
.version('0.0.1')
.option('-p, --phrase [value]', 'phrase')
.option('-c, --count <n>', 'Number of Times To Repeat Phrase', parseInt)
.parse(process.argv);
/**
* Multiplies string with additional space.
* @param {string} phrase The phrase.
* @param {number} count The number of times to repeat
* @returns {string} The multiplied string
*/
function phraseGenerator (phrase, count) {
return phrase.concat(" ").repeat(count);
}
// Check to see both options are used
if (typeof program.phrase === 'undefined' ||
typeof program.count === 'undefined') {
console.error('ERROR! --phrase and --count options required');
program.help();
process.exit(1);
}
// Print Phrase To Standard Out
console.log(phraseGenerator(
program.phrase,
program.count));
Next there is a package.json
file.
{
"name": "nodecli",
"version": "1.0.0",
"description": "nodecli",
"main": "index.js",
"dependencies": {
"commander": "^2.10.0"
},
"devDependencies": {
"eslint": "^4.1.1",
"eslint-config-defaults": "^9.0.0"
},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "git+https://github.com/noahgift/nodecli.git"
},
"keywords": [
"cli"
],
"author": "Noah Gift",
"license": "MIT",
"bugs": {
"url": "https://github.com/noahgift/nodecli/issues"
},
"homepage": "https://github.com/noahgift/nodecli#readme"
}
To run the example, you would do the following.
Steps to run:
npm install
./hello-world --phrase "hello world" --count 3
The output should be:
hello world hello world hello world
Multi-paradigm Node.js #
Getting Started #
This application source code is here.
To build this project, you need to:
npm install
To talk to the blockchain network, please run ./startFabric.sh. Then ./query-cli.js. Further information and background of this forked version can be found here
Features #
- Color Output
- JSON Formatting
- Async Network Operations
- Blockchain Integration
Screenshots #
- Screenshot No Options:
- Screenshot One Option:
The source is below. You can see how a more sophisticated node
tool integrates with both blockchain, and colored output works.
#!/usr/bin/env node
"use strict";
/*
Hyperledger Query Commandline Tool
./query-cli.js
__dirname = path.resolve();
*/
const Hfc = require('fabric-client'),
path = require('path'),
chalk = require('chalk'),
prettyjson = require('prettyjson'),
program = require('commander'),
options = {
walletPath: path.join(__dirname, './network/creds'),
userId: 'PeerAdmin',
channelId: 'mychannel',
chaincodeId: 'fabcar',
networkUrl: 'grpc://localhost:7051'
};
let channel = {},
transactionId = null,
client = null,
jsonResult = null;
program
.version('0.0.1')
.option('-c, --car [value]', 'car to query')
.parse(process.argv);
/**
* Queries Blockchain
* @param {string} chaincodeFunc The chaincode function to query
* @param {string} car The individual car to query
* @returns {string} nothing
*/
function queryHelper (chaincodeFunc = 'queryAllCars', car = '') {
Promise.resolve().then(() => {
console.log(chalk.red("Create a client and set the wallet location"));
client = new Hfc();
return Hfc.newDefaultKeyValueStore({path: options.walletPath});
})
.then((wallet) => {
console.log(chalk.red("Set wallet path, and associate user ",
options.userId, " with application"));
client.setStateStore(wallet);
return client.getUserContext(options.userId, true);
})
.then((user) => {
console.log(
chalk.red(
"Check user is enrolled, and set a query URL in the network"
));
if (typeof user === "undefined" || user.isEnrolled() === false) {
console.error("User not defined, or not enrolled - error");
}
channel = client.newChannel(options.channelId);
channel.addPeer(client.newPeer(options.networkUrl));
})
.then(() => {
console.log(chalk.red("Make query"));
transactionId = client.newTransactionID();
console.log(chalk.red("Assigning transaction_id: "),
chalk.green(transactionId._transaction_id));
// The queryCar - requires 1 argument, ex: args: ['CAR4'],
// The queryAllCars - requires no arguments , ex: args: [''],
const request = {
chaincodeId: options.chaincodeId,
txId: transactionId,
fcn: chaincodeFunc,
args: [car]
};
return channel.queryByChaincode(request);
})
.then((queryResponses) => {
console.log(chalk.red("returned from query"));
if (typeof queryResponses.length === 'undefined') {
console.log("No payloads were returned from query");
} else {
console.log(
chalk.bgBlue("Query result count = ", queryResponses.length));
}
if (queryResponses[0] instanceof Error) {
console.error("error from query = ", queryResponses[0]);
}
jsonResult = JSON.parse(queryResponses[0].toString());
console.log(prettyjson.render(jsonResult, {
keysColor: 'yellow',
dashColor: 'blue',
stringColor: 'white'
}));
})
.catch((err) => {
console.error("Caught Error", err);
});
}
// Run The Command line Tool
if (typeof program.car === 'undefined') {
queryHelper('queryAllCars');
} else {
queryHelper('queryCar', program.car);
}
Python Hello World #
Recommended environment #
Python 3.6.1
Running #
Steps to Run:
Install packages:
make install
Activate Virtual Env:
source ~/.hello-world-py-cli/bin/activate
Run Tool:
./hello-world.py --phrase "hello world" --count 3
The output should be:
hello world hello world hello world
The `Makefile looks like:
install:
mkdir -p ~/.hello-world-py-cli &&\
python3 -m venv ~/.hello-world-py-cli &&\
pip install -r requirements.txt
source-cmd:
echo "Virtualenv source command"
#source ~/.hello-world-py-cli/bin/activate
lint:
pylint hello-world.py
The requirements.txt file looks like:
click
pylint
Finally, the python
code is as follows.
#!/usr/bin/env python
import click
@click.version_option("0.1")
@click.group()
def cli():
"""Hello World"""
@cli.command("hello")
@click.option("--phrase", help="phrase to print")
@click.option("--count", help="Number of times to repeat phrase", type=int)
def hello(phrase, count):
"""Hello World Command-line tool"""
while count:
count -= 1
click.echo(phrase)
if __name__ == '__main__':
cli()
A full example of lint and run:
(.hello-world-py-cli) $ hello-world git:(master) $ ./hello-world.py hello\
--phrase "hello world" --count 3
hello world
hello world
hello world
(.hello-world-py-cli) $ hello-world git:(master) $ make lint
pylint hello-world.py
-------------------------------------------------------------------
Your code has been rated at 10.00/10 (previous run: 7.50/10, +2.50)
You can find the latest up to date examples in the Github Repo https://github.com/noahgift/cli-rosetta.