Skip to content
Image with logo, providing a link to the home page
  • United Stated of America flag, representing the option for the English language.
  • Bandeira do Brasil, simbolizando a opção pelo idioma Português do Brasil.

Learn Programming: Command Line Input (Command Line Arguments and Interactivity)

Example of using command line input in four programming languages: Python, Lua, GDScript and JavaScript.

Image credits: Image created by the author using the program Spectacle.

Requirements

In the introduction to development environments, I have mentioned Python, Lua and JavaScript as good choices of programming languages for beginners. Later, I have commented about GDScript as an option for people who want to program digital games or simulations. For the introductory programming activities, you will need, at least, a development environment configured for one of the previous languages.

If you wish to try programming without configuring an environment, you can use of the online editors that I have created:

However, they do not provide all features offered by interpreters for the languages. Thus, sooner or later, you will need to set up a development environment. If you need to configure one, you can refer to the following resources.

Thus, if you have an Integrated Development Environment (IDE), or a combination of text editor and an interpreter, you are ready to start. The following example assumes that you know how to run code in your chosen language, as presented in the configuration pages.

If you want to use another language, the introduction provides links for configure development environments for the C, C++, Java, LISP, Prolog, and SQL (with SQLite) languages. In many languages, it suffices to follow the models from the experimentation section to modify syntax, commands and functions from the code blocks. C and C++ are exceptions, for they require pointers access the memory.

Old School Automation for the Future

For many people, the command line is an archaic magical artifact; an inheritance of the dawning of Computation. For end-users, this can be a reality. For developers, it is not.

In 2022, the command is still effective and efficient to perform common programming tasks. Automation, data exchange, data conversion, and server operations are some examples. Even in personal projects, the command line is required to use package managers (as the ones presented in Libraries) and source-control management systems.

Instead of fearing or running from the command line, it is worth embracing it as a programming tool. The efforts required to learn it will be rewarded over time. In particular, the command line allows noticing how the operation of graphical interfaces are limited (and inefficient). The possibility of storing sequences of commands as macros or in a script allows repeating solutions using multiple programs as if they were subroutines in a programming language.

The command line is, therefore, still a modern resource for automation. Henceforth, you will be able to integrate your programs to the efficient command line, once they start receiving values as input from a command line interpreter (shell).

Naming

Programs with graphical interface are often referred by the acronym GUI, which means graphical user interface. Programs with textual interface have a similar acronym: TUI, meaning text-based user interface. Programs that are operated using a command line are normally referred as having a command-line interface or CLI.

Unix Philosophy

When one works with the command line, she/he may find worth to know about the Unix philosophy. The following paragraph cites the Wikipedia citation:

"Write programs that do one thing and do it well. Write programs to work together. Write programs to handle text streams, because that is a universal interface."

From the Portuguese entry, "or, to put it simply, do only one thing and do it well".

The principle of restricting a program to perform a single task efficiently is a powerful one. Instead of thinking in programs with multiple features, one can think in program as if it was a subroutine -- or an algorithm with a single purpose. Given a set of inputs, it calculates and returns the output.

When one adopts the Unix philosophy, she/he can compose programs as sequences of executions of programs. In other words, instead of programming using programming languages, she/he can program using specialized programs. Tools.

For instance, command interpreters provide an operator called pipe (or pipeline, for the mechanism). The metaphor is of a pipe that redirects the output of a program to the input of another program. This makes it possible to run programs in sequence, on which the output of previous programs are intermediate results that work as inputs for the next commands.

For instance, echo allows writing a message. wc count lines, words, and characters. awk allows, among other features, extracting fields in text. By combining the three programs as commands, one can find the number of words in a phrase. This can be achieved using the pipe, which is commonly represented by a vertical bar (|) as the operator.

echo "Hello, my name is Franco" | wc | awk '{print $2}'
5

With some adaptations, one can find the number of words in a text file.

echo "Hello, my name is Franco" > file.txt
cat file.txt | wc | awk '{print $2}'
5
wc < file.txt | awk '{print $2}'
5

In the second example, the operator with the greater than symbol (>) redirects the standard output to a file. Thus, the first line is equivalent to creating a file named file.txt with the contents Hello, my name is Franco. The commands from the second and third line are equivalent. The first version uses a pipe: cat reads the contents of the file, which are passed to wc by another pipe. In the third line, the operator with the less than symbol (<) redirects the contents of a file to the standard input. Thus, it works as if someone was typing the contents of the type as the input to the program wc.

The command line is, therefore, a tool for automation and system administration. From previous topics, one knows how to write data as command line output using subroutines or commands such as print(). She/he also knows how to request the input of data in a process (running program);this is called interactive input. To complete the cycle, she/he should learn how to receive input from the command line that is provided in a non-interactive way by end-users.

Command Line Input

The topic Console (Terminal) Input has presented how to read data in an interactive way in a program that is running.

Command line input is a different approach to input, on which one provides values to the program as part of the command to run it. In non-interactive CLI programs, the values are passed when starting the program; then one awaits the result (which is provided when the program ends).

For instance, a command such as date in Linux provides the current date and time.

date
qui 10 fev 2022 12:22:33 -03

The command also provide several parameters to configure the output. For instance, if one passes a value such as --iso-8601, the data is formatted in the ISO 8601 standard; with --rfc-3339, the date is formatted according to the RFC 3339.

date --iso-8601
2022-02-10
date --rfc-3339=seconds
2022-02-10 12:23:45-03:00

In both cases, the value passed after the name of the program acts a parameter that, similar to an interactive input, allows changing the execution flow of a program.

If you do not use an IDE to run your programs, you provide as parameter to an interpreter the name o the file to be run. For instance, lua file.lua. The process is the same; file.lua is the parameter that specifies the file that will be used as input. If lua was used, it would run the interpreter in the REPL mode.

In particular, if you adopt this same strategy in your programs, it is possible to create new tools for the command line -- and use them along (or in complement) to every other provided by the shell.

JavaScript (Node.js), Python, Lua and GDScript

A Web browser is not a command line interpreter. Thus, the version in JavaScript uses Node.js as the JavaScript interpreter. Node.js has been installed in the topic Libraries.

// node script.js text 1 1.23

let arguments = process.argv
for (let index = 0; index < arguments.length; ++index) {
    let argument = arguments[index]
    console.log(index, argument)
}
# python script.py text 1 1.23

import sys

index = 0
arguments = sys.argv
for argument in arguments:
    print(index, argument)
    index += 1
-- lua script.lua text 1 1.23

local arguments = arg
for index, argument in ipairs(arguments) do
    print(index, argument)
end
# godot -s script.gd text 1 1.23

extends SceneTree

func _init():
    var arguments = OS.get_cmdline_args()
    var index = 0
    for argument in arguments:
        printt(index, argument)
        index += 1

    quit()

The first line of each program suggests a command to test it. The values can be changed to any others.

To make it easier to compare implementations, all arguments received from the command line have been assigned to the variable arguments. It is not necessary to create it; one could use the corresponding variable in each programming language.

In JavaScript with Node.js, the received arguments are saved in the array process.argv (documentation). In Python, the arguments are saved in the list sys.argv (documentation). In Lua, the arguments are saved in the global table args (documentation). In GDScript, the arguments are accessed using OS.get_cmdline_args() (documentation). In GDScript, one should be careful not to choose arguments' names that are used by the editor (documentation). The GDScript version uses SceneTree instead of Node (or other type) to allow running the program in a command line interpreter without requiring a project. This has been previously commented in development environment configuration for GDScript (Godot) and in the topic Entry Point and Program Structure.

The name argv is popular as it is traditional in the C programming language. In C, the main() function can receive two parameters: argv, that is an array of strings storing the values received from the command line, and argc, which corresponds to the size of argv. Although it is possible to change the names of the variables, argv and argc are the names adopted by default.

It is simple to read values from the command line. After identifying the variable that store the received values, it suffices processing it as any other array. The position of the first received value can vary between interpreters and programming languages. Often, the first position of the array stores the name of the program executed (or the path to it). Thus, the first position will not always be the first value received as argument.

To use the programs, it is necessary to provide values during the initialization. In other words, one should not double-click to start the program, though use a command line interpreter to run the program (or use a shortcut or script that defines the parameters). When a program is started using a command line interpreter, one writes the name of the program (or the path to it). Any values written after the name of the program are parameters that the program receives as command line arguments. In a generic way:

./program_name parameter_1 parameter_2 other_parameters

For interpreted languages, program_name will be the name of the interpreter. The first parameter that is passed will be the file to be interpreted (or some configuration flag, communicating that it is a script). Every other parameter will be passed as values as input for the program (unless they are also flags for the interpreter).

The following examples illustrate the outputs of the programs defined as examples. One can notice that the output can vary between interpreters, as can the index that stores the first parameter value.

node script.js text 1 1.23
0 /usr/bin/node
1 /home/franco/tmp/js/script.js
2 text
3 1
4 1.23
python script.py text 1 1.23
0 script.py
1 text
2 1
3 1.23
lua script.lua text 1 1.23
1       text
2       1
3       1.23
godot -s script.gd text 1 1.23
Godot Engine v3.4.2.stable.arch_linux - https://godotengine.org
OpenGL ES 3.0 Renderer: NVIDIA GeForce GTX 1080/PCIe/SSE2
OpenGL ES Batching: ON

0       -s
1       script.gd
2       text
3       1
4       1.23

For GDScript, if one does not want to open a window, she/he can pass --no-window before -s. For instance, godot --no-window -s script.gd text.

It is important noticing that every value is received by the program as a string. To retrieve that value as another type, it suffices to convert it to the expected data type. This has been discussed previously, in Data Types.

Furthermore, if one wishes to use spaces, she/he can provide the value surrounded by quotes (normally command line interpreters accept single or double quotes, though the support can vary depending on the program). Likewise, it is possible to use a backslash as an escape character for characters or values that are considered special by the command line interpreter. For instance:

node script.js "Hello, my name is Franco\!"
0 /usr/bin/node
1 /home/franco/tmp/js/script.js
2 Hello, my name is Franco!
python script.py "Hello, my name is Franco\!"
0 script.py
1 Hello, my name is Franco
lua script.lua "Hello, my name is Franco\!"
1       Hello, my name is Franco!
godot -s script.gd "Hello, my name is Franco\!"
Godot Engine v3.4.2.stable.arch_linux - https://godotengine.org
OpenGL ES 3.0 Renderer: NVIDIA GeForce GTX 1080/PCIe/SSE2
OpenGL ES Batching: ON

0       -s
1       script.gd
2       Hello, my name is Franco!

With the change, the message Hello, my name is Franco! is passed as a single value (instead of being parsed as one value after each space).

Command Line Parameters in IDEs

IDEs usually provide features to set command line parameters that will be provided to the program. This varies from IDE to IDE; thus, it is worth consulting the documentation.

In the IDE Thonny for Python, the option can be enabled in View, then Program arguments. The option will add a text field called Program arguments: to the IDE. One can write the desired parameters in the field (for instance, text 1 1.23).

In the IDE ZeroBrane Studio for Lua, there are some options to set the parameters, which are described in the documentation. One of the simplest is accessing Project, then choose the option Command Line Parameters.... In the dialog that will appear, one must write the arguments (for instance, text 1 1.23) and confirm,

For GDScript, Godot Engine can be configured in Project, Project Settings, Editor, Main Run Args. The easiest way to find the option is to use the search in the project settings. After one finds it, she/he must write the desired value (for instance, text 1 1.23) and close the configuration. This makes it possible using arguments in _ready().

extends Node

func _ready():
    var arguments = OS.get_cmdline_args()
    var index = 0
    for argument in arguments:
        printt(index, argument)
        index += 1

One should not that arguments apply to the program; thus, the values will be the same for all uses, regardless of script.

JavaScript For Browsers: URL Parameters

Although is it not possible to use command line parameters in JavaScript, there is an alternative. In browsers, one can pass parameters to the address of a request, using the Uniform Resource Locator (URL). The easiest way to do this consists of using the request method GET. The use of parameters in requests is commonly used to create forms in Web pages. With some creativity, it can be explored to simulate command line parameters in browsers' URLs.

The example in JavaScript define a file named script.js and the HTML in any file (for instance, index.html, used in snippet). To use the page, one must type the address as: index.html?text=text&integer=1&real=1.23. text=, integer= e real= are the desired name to query the value of the received parameter. One can choose the name as she/he wants it, using the pattern name=value. Multiple values are separated by an & (ampersand).

let parameters_text = window.location.search
console.log(parameters_text)

const parameters = new Proxy(
    new URLSearchParams(window.location.search),
                        {
                            get: (target, name) => target.get(name),
                        })
console.log(parameters.text, parameters.integer, parameters.real)
console.log(parameters.text, Number(parameters.integer), Number(parameters.real))
<!DOCTYPE html>
<html lang="en-US">
  <head>
    <meta charset="utf-8">
    <title>URL Parameters for JavaScript</title>
    <meta name="author" content="Franco Eusébio Garcia">
    <meta name="viewport" content="width=device-width, initial-scale=1">
  </head>
  <body>
    <script src="./script.js"></script>
  </body>
</html>

The values are provided as a string stored in window.location.search (documentation). In modern browsers, a way of extracting the value consists of using Proxy (documentation). To access the value, one creates an instance of Proxy, then use the name that has been defined for each query.

Examples

CLI programs are common, especially as tools for programmers. Thus, there are libraries that ease the creation of these programs in several programming languages. The libraries may provide facilities such as features for extracting values provided as parameters or displaying values in textual format (for instance, with different colors). Two traditional options include getopt and argp, from the GNU Project.

GNU also defines CLI standards, that are common in many command line tools. An example is -h or --help, which is used to display the help. For instance, python --help (or python -h), lua --help (or lua -h), node --help (or node -h), and godot --help (or godot -h) print the help options for each previous program, listing possible parameters that can be used when starting the program. The GNU standards are common on Linux systems; other systems may have other conventions. For instance, on Windows, programs (especially older ones) typically use the pattern /option as the code for an option (this would be equivalent to --option). For instance, the help option is commonly defined as /?.

There also libraries that ease the creation of "graphical" interfaces using text. One of the most traditional is called ncurses. The name means new curses, because it is an improved version of the curses library. ncurses allows creating interactive TUI programs for the command line with sophisticated interfaces (for text standards).

As always, it is wroth knowing how libraries implement basic features.

Configuration Using Key and Value

For complex programs, the use of libraries that make it easier implementing a CLI can be recommended. For simple programs, it can be enough combining string operations with conditional structures to select values. Therefore, the second situation may be considered for learning purposes.

In command line programs, two features are common for settings:

  • Flags to enable or disable features, or show a particular option. For instance, -h to show the help and quit.

  • Pairs of key and value, to configure or set values for configuration. The typical for tends to use one of the following two possibilities:

    1. --name=value or -n=value;
    2. --name value or -n value.

    On Windows, the values can follow the format /name value.

    For pairs of key and value, the first case assumes that the value is provided after a specific character (delimited) such as an equal sign. As arguments are typically passed as arrays to the program, it suffices to analyze a single index to extract the value.

    The second case assumes that the value is provided after a delimiter such a space. In this case, one must consult the index of the option to identify the key, and the next index to retrieve the value.

    In both cases, if value has spaces, it must be passed surrounded by (single or double) quotes.

For a simple example, one can consider a program that generates a greeting. The program can have options such as:

  1. --greeting or -g. The default value can be Hello!. If one uses something like --greeting=Hi, the program will use the value Hi instead of Hello!;
  2. --message or -m. The default value can be My name is Franco.. If one uses something like --message="My name is Ana", the program will use the defined message instead of the default one;
  3. --skip-line or -s. The default can be writing text in a single line. If the one specifies the option, the program adds a new line between the greeting and the message. If omitted, the values are printed in the same line.
  4. --help or -h. Shows a help message and quits the program.

In general, one can pass parameters to the command line in any order. Thus, one cannot assume which one will be provided first. It is also common that programs define which will be the last expected parameter. This makes it easier to use pipes, as well as programming interpreters (after the script file, eventual remaining options are meant to the program, instead of for the interpreter). Evidently, each program has the liberty to define its own conventions, provided that they are documented.

// Examples of use:
// node script.js
// node script.js --skip-line --greeting=Hi. --message="My name is X"
// node script.js --message="My name is X"
// node script.js --greeting=Hi. --skip-line

const EXIT_FAILURE = 1

let skip_line = false
let greeting = "Hello!"
let message = "My name is Franco."

for (argument of process.argv.slice(2)) {
    if ((argument.startsWith("--help")) || (argument.startsWith("-h"))) {
        console.log("Usage:")
        console.log("--greeting=...\tUses the chosen value as greeting.")
        console.log("--message=... \tUses the chosen value as message.")
        console.log("--skip-line   \tAdds a line break between the greeting and the message.")
        console.log("--help        \tShows this text and exit the program.")
        process.exit()
    }

    if ((argument.startsWith("--greeting=")) || (argument.startsWith("-g="))) {
        greeting = argument.slice(argument.indexOf("=") + 1)
    } else if ((argument.startsWith("--message=")) || (argument.startsWith("-m="))) {
        message = argument.slice(argument.indexOf("=") + 1)
    } else if ((argument == "--skip-line") || (argument == "-s")) {
        skip_line = true
    } else {
        console.log("Error:")
        console.log("Unknown option: " + argument)
        process.exit(EXIT_FAILURE)
    }
}

if (skip_line) {
    console.log(greeting)
    console.log(message)
} else {
    console.log(greeting + " " + message)
}
# Examples of use:
# python script.py
# python script.py --skip-line --greeting=Hi. --message="My name is X"
# python script.py --message="My name is X"
# python script.py --greeting=Hi. --skip-line

import sys

EXIT_FAILURE = 1

skip_line = False
greeting = "Hello!"
message = "My name is Franco."

for argument in sys.argv[1:]:
    if ((argument.startswith("--help")) or (argument.startswith("-h"))):
        print("Usage:")
        print("--greeting=...\tUses the chosen value as greeting.")
        print("--message=... \tUses the chosen value as message.")
        print("--skip-line   \tAdds a line break between the greeting and the message.")
        print("--help        \tShows this text and exit the program.")
        sys.exit()

    if ((argument.startswith("--greeting=")) or (argument.startswith("-g="))):
        greeting = argument[argument.find("=") + 1:]
    elif ((argument.startswith("--message=")) or (argument.startswith("-m="))):
        message = argument[argument.find("=") + 1:]
    elif ((argument == "--skip-line") or (argument == "-s")):
        skip_line = True
    else:
        print("Error:")
        print("Unknown option: " + argument)
        sys.exit(EXIT_FAILURE)

if (skip_line):
    print(greeting)
    print(message)
else:
    print(greeting + " " + message)
-- Examples of use:
-- lua script.lua
-- lua script.lua --skip-line --greeting=Hi. --message="My name is X"
-- lua script.lua --message="My name is X"
-- lua script.lua --greeting=Hi. --skip-line

function starts_with(text, valor)
    return (string.find(text, valor, 1, true) == 1)
end

local EXIT_FAILURE = 1

local skip_line = false
local greeting = "Hello!"
local message = "My name is Franco."

for _, argument in ipairs(arg) do
    if ((starts_with(argument, "--help")) or (starts_with(argument, "-h"))) then
        print("Usage:")
        print("--greeting=...\tUses the chosen value as greeting.")
        print("--message=... \tUses the chosen value as message.")
        print("--skip-line   \tAdds a line break between the greeting and the message.")
        print("--help        \tShows this text and exit the program.")
        os.exit()
    end

    if ((starts_with(argument, "--greeting=")) or (starts_with(argument, "-g="))) then
        greeting = string.sub(argument, string.find(argument, "=", 1, true) + 1, #argument)
    elseif ((starts_with(argument, "--message=")) or (starts_with(argument, "-m="))) then
        message = string.sub(argument, string.find(argument, "=", 1, true) + 1, #argument)
    elseif ((argument == "--skip-line") or (argument == "-s")) then
        skip_line = true
    else
        print("Error:")
        print("Unknown option: " .. argument)
        os.exit(EXIT_FAILURE)
    end
end

if (skip_line) then
    print(greeting)
    print(message)
else
    print(greeting .. " " .. message)
end
# Examples of use:
# godot -s script.gd
# godot -s script.gd --skip-line --greeting=Hi. --message="My name is X"
# godot -s script.gd --message="My name is X"
# godot -s script.gd --greeting=Hi. --skip-line

extends SceneTree

const EXIT_FAILURE = 1

func _init():
    var skip_line = false
    var greeting = "Hello!"
    var message = "My name is Franco."
    var success = true
    var should_quit = false

    var arguments = Array(OS.get_cmdline_args())
    arguments = arguments.slice(2, arguments.size())

    for argument in arguments:
        if ((argument.begins_with("--help")) or (argument.begins_with("-h"))):
            print("Usage:")
            print("--greeting=...\tUses the chosen value as greeting.")
            print("--message=... \tUses the chosen value as message.")
            print("--skip-line   \tAdds a line break between the greeting and the message.")
            print("--help        \tShows this text and exit the program.")
            should_quit = true

        if ((argument.begins_with("--greeting=")) or (argument.begins_with("-g="))):
            greeting = argument.substr(argument.find("=") + 1)
        elif ((argument.begins_with("--message=")) or (argument.begins_with("-m="))):
            message = argument.substr(argument.find("=") + 1)
        elif ((argument == "--skip-line") or (argument == "-n")):
            skip_line = true
        else:
            print("Error:")
            print("Unknown option: " + argument)
            success = false

    if (success):
        if (not should_quit):
            if (skip_line):
                print(greeting)
                print(message)
            else:
                print(greeting + " " + message)

        quit()
    else:
        quit(EXIT_FAILURE)

After the previous topics, the implementation should be simple. The only special detail is that, in some languages, the iteration has started in the position that effectively store the first argument that has been received from the command line.

In case of error, the execution is finished with a non-zero value, as commented about EXIT_FAILURE in Files and Serialization (Marshalling). When one uses command line programs, this is useful to cancel the execution of programs in sequence if one of them fails. The version in JavaScript uses process.exit() (documentation) to end the process.

The version with space is similar, albeit it requires iterating on the indices of the array to read the next value (when necessary). You can try implementing it.

Furthermore, in a careful examination, one can notice that the code to extract parameter values is repeated. To avoid the repetition, one could implement a function that extracted the value if it matched the expected one for the key. She/he could introduce the function and refactor the code.

Fixes for Parameters Set by the Editor in GDScript

The version in GDScript has some particularities. It uses -n instead of -n, because -s is a default option in Godot Engine. Furthermore, if one adjusts the command line parameters in the editor, it may be necessary to adjust the index of the first argument, and to manually combine strings grouped by quotes and separated by a space. To do this, instead of copying the received parameter, one can concatenate the string until the end of the quotes.

extends Node
const EXIT_FAILURE = 1

func _ready():    var skip_line = false
    var greeting = "Hello!"
    var message = "My name is Franco."
    var success = true
    var should_quit = false

    var arguments = []    var new_argument = true    var argument_with_spaces = null    for argument in OS.get_cmdline_args():        if (new_argument):            if (argument.find("=\"") == -1):                arguments.append(argument)            else:                argument_with_spaces = argument.replace("=\"", "=")                new_argument = false        else:            argument_with_spaces += " " + argument            new_argument = argument.ends_with("\"")            if (new_argument):                arguments.append(argument_with_spaces.substr(0, len(argument_with_spaces) - 1))
    for argument in arguments:
        if ((argument.begins_with("--help")) or (argument.begins_with("-h"))):
            print("Usage:")
            print("--greeting=...\tUses the chosen value as greeting.")
            print("--message=... \tUses the chosen value as message.")
            print("--skip-line   \tAdds a line break between the greeting and the message.")
            print("--help        \tShows this text and exit the program.")
            should_quit = true

        if ((argument.begins_with("--greeting=")) or (argument.begins_with("-g="))):
            greeting = argument.substr(argument.find("=") + 1)
        elif ((argument.begins_with("--message=")) or (argument.begins_with("-m="))):
            message = argument.substr(argument.find("=") + 1)
        elif ((argument == "--skip-line") or (argument == "-n")):
            skip_line = true
        else:
            print("Error:")
            print("Unknown option: " + argument)
            success = false

    if (success):
        if (not should_quit):
            if (skip_line):
                print(greeting)
                print(message)
            else:
                print(greeting + " " + message)

        get_tree().quit()    else:
        get_tree().quit(EXIT_FAILURE)

The example only consider double quotes. To also consider single quotes, it should be modified. Besides, for a better implementation, intermediate patterns such as \" should be ignored, as they are escaped quotes.

Integration with the Command Line

A program called Cowsay was mentioned in Libraries. The output of the program was an ASCII Art drawing of a cow saying a phrase passed as a parameter.

The creation of a program similar to Cowsay can be useful to illustrate a hybrid approach exploring command line parameters with interactive input. Instead of a cow, it will use the drawing of bunnies.

As GDScript does support reading values from a terminal, the implementation for the language will only use the command line parameter. In the other languages, the program will request a message as input if no parameter is provided.

If you use a screen reader to head the content of this page, the command or subroutines used to draw the bunny will not make much sense. They use slashes, parentheses, quotes and underscores to draw the bunny.

// Examples of use:
// node script.mjs
// mpde script.mjs "Hi! How are you?"

import * as readline from "node:readline/promises"
import { stdin, stdout } from "node:process"

let message = ""
if (process.argv.length < 3) {
    const rl = readline.createInterface({input: stdin, output: stdout})
    message = await rl.question("Type a message: ")
    rl.close()
} else {
    message = process.argv[2]
}

if (message === "") {
    message = "Hello, my name is Franco!"
}

console.log('')
console.log('        ' + message)
console.log('       /  ')
console.log('(\\_/) /')
console.log('( ^_^)')
console.log('c(")(")')
# Examples of use:
# python script.py
# python script.py "Hi! How are you?"

import sys

message = ""
if (len(sys.argv) < 2):
    message = input("Type a message: ")
else:
    message = sys.argv[1]

if (message == ""):
    message = "Hello, my name is Franco!"

print('')
print('        ' + message)
print('       /  ')
print('(\\_/) /')
print('( ^_^)')
print('c(")(")')
-- Examples of use:
-- lua script.lua
-- lua script.lua "Hi! How are you?"

local message = ""
if (#arg < 1) then
    io.write("Type a message: ")
    message = io.read("*line")
else
    message = arg[1]
end

if (message == "") then
    message = "Hello, my name is Franco!"
end

print('')
print('        ' .. message)
print('       /  ')
print('(\\_/) /')
print('( ^_^)')
print('c(")(")')
# Examples of use:
# godot -s script.gd
# godot -s script.gd "Hi! How are you?"

extends SceneTree

func _init():
    var message = "My name is Franco."
    if (len(OS.get_cmdline_args()) > 2):
        message = OS.get_cmdline_args()[2]

    print('')
    print('        ' + message)
    print('       /  ')
    print('(\\_/) /')
    print('( ^_^)')
    print('c(")(")')

    quit()

A better implementation could check if message only contains black spaces instead of being equal to an empty string, as it has been done previously. Nevertheless, the intent of the implementation is showing that the interactive read happens only if a value is not provided by the command line. This is performed by checking the number of arguments that have been received by the program.

This JavaScript version using Node.js needs a .mjs extension (for instance, script.mjs). Otherwise, one must use require() instead of import(). Regardless, reading values in the terminal with Node.js can use readline (documentation).

The version for GDScript only works in the command line. To configure it in the editor, one would need to fix the received indices, as it has been mentioned in the previous section.

To improve the program, one can add pairs of keys and values, or options to enable or disable. For instance, there could be parameters to customize the eyes, draw a balloon around the message, or to change the ASCII Art drawing.

The next snippet provides some suggestions of alternative ways to draw the bunny.

(\)(/)    (\_/)     (\_/)     (\_/)
( ^_^)    ( o_o)    ( ._.)    ( ^.^)
c(")(")   c(")(")   c(")(")   c(")(")

One must remember to use \\ to write a backslash.

Using the Program with Pipes

After the program is finished, it can be used along with other command line tools available in the system. For instance, on a Linux system, one could use:

uname -a | python script.py
Type a message:
        Linux darkstar-6700k 5.16.5-arch1-1 #1 SMP PREEMPT Tue, 01 Feb 2022 21:42:50 +0000 x86_64 GNU/Linux
       /
(\_/) /
( ^_^)
c(")(")

To draw a bunny saying the date and time on a Linux system one could use:

date | lua script.lua
Type a message:
        qui 10 fev 2022 16:32:40 -03
       /
(\_/) /
( ^_^)
c(")(")

The call could be modified to use versions for other programming languages.

Unix-like systems such as Linux follow the Unix philosophy. Thus, there are countless possibilities to integrate programs with command line input with the other CLI tools.

In other systems, such as Windows and macOS, one can use equivalent commands with the previous examples. For instance, the cmd interpreter for Windows provide the commands date and time to retrieve the system's date and time, respectively. date /t and time /t provide the date and time without requesting new values to be set. Assuming that lua (or lua.exe) is available in the PATH, the next block provides an example of use.

date /t | lua script.lua
Type a message:
         12/02/2022
       /
(\_/) /
( ^_^)
c(")(")

To combine date and time, one could use cmd variables and concatenate intermediate results. Another possibility is using echo with %date% and %time, that are variables that provide the current date and time, respectively.

echo %date% %time% | lua script.lua
Type a message:
         12/02/2022 12:21:39,12
       /
(\_/) /
( ^_^)
c(")(")

Alternatively, one can use Get-Date in PowerShell (instead of cmd).

Adding a Silent Mode

Command line programs may offer a mode called quiet, which inhibits writing data to the standard output (stdout). The option is usually named --quiet or -q. As a curiosity, the reverse of the option is called verbose mode, with an option --verbose; -v is often equal to --version, which provides the version.

To add a similar option to the program, the following examples define the option --silent (or -s). A real quiet mode does not make sense for the program, because its purpose is writing a message and draw ASCII Art. However, the idea is still applicable to a particular situation. In the original version, input received from a pipe uses the standard input (stdin), and, therefore, it displays a message for the person who would use the program.

Although one could remove the message, another alternative consists of adding a flag as parameter that, when enabled, does not write the text requesting the input. The example will not be implemented in GDScript, because the language does not allow reading values in a terminal.

// Examples of use:
// node script.mjs
// node script.mjs --silent
// mpde script.mjs "Hi! How are you?"

import * as readline from "node:readline/promises"
import { stdin, stdout } from "node:process"

let show_message = true

let message = ""
let argc = process.argv.length
if (argc >= 3) {
    let argument = process.argv[2]
    if ((argument === "--silent") || (argument === "-s")) {
        show_message = false
        if (argc > 3) {
            message = process.argv[3]
        }
    } else {
        message = argument
    }
}

if (message === "") {
    if (show_message) {
        let rl = readline.createInterface({input: stdin, output: stdout})
        message = await rl.question("Type a message: ")
        rl.close()
    } else {
        let rl = readline.createInterface({input: stdin})
        message = await rl.question("")
        rl.close()
    }
}

if (message === "") {
    message = "Hello, my name is Franco!"
}

console.log('        ' + message)
console.log('       /  ')
console.log('(\\_/) /')
console.log('( ^_^)')
console.log('c(")(")')
# Examples of use:
# python script.py
# python script.py --silent
# python script.py "Hi! How are you?"

import sys

show_message = True

message = ""
argc = len(sys.argv)
if (argc >= 2):
    argument = sys.argv[1]
    if ((argument == "--silent") or (argument == "-s")):
        show_message = False
        if (argc > 2):
            message = sys.argv[2]
    else:
        message = argument

if (message == ""):
    if (show_message):
        print("Type a message: ", end="")

    message = input()

if (message == ""):
    message = "Hello, my name is Franco!"

print('        ' + message)
print('       /  ')
print('(\\_/) /')
print('( ^_^)')
print('c(")(")')
-- Examples of use:
-- lua script.lua
-- lua script.lua --silent
-- lua script.lua "Hi! How are you?"

local show_message = true

local message = ""
local argc = #arg
if (argc >= 1) then
    local argument = arg[1]
    if ((argument == "--silent") or (argument == "-s")) then
        show_message = false
        if (argc >= 2)  then
            message = arg[2]
        end
    else
        message = argument
    end
end

if (message == "") then
    if (show_message) then
        io.write("Type a message: ")
    end

    message = io.read("*line")
end

if (message == "") then
    message = "Hello, my name is Franco!"
end

print('        ' .. message)
print('       /  ')
print('(\\_/) /')
print('( ^_^)')
print('c(")(")')

The JavaScript version omits the configuration of output to avoid echoing the message in the silent mode. Now, one can use the parameter --silent or -s to hide the message when using a pipe.

echo "Silence stopped by..." | lua script.lua --silent
        Silence stopped by...
       /
(\_/) /
( ^_^)
c(")(")

echo "Silence stopped by..." | python script.py --silent
        Silence stopped by...
       /
(\_/) /
( ^_^)
c(")(")

echo "Silence stopped by..." | node script.mjs --silent
        Silence stopped by...
       /
(\_/) /
( ^_^)
c(")(")

The inclusion of new settings works similarly. It is worth noticing that the message has been left as the last parameter as a convenience.

Interactive Interface for Terminal (Console) with ncurses

For a different example, the next snippet presents a simple program using the curses library in Python (documentation). For another similar library, one can consult Urwird.

Although there are versions of the library for other languages, the module curses can be used in Python without further configuration on Linux and macOS systems. On Windows, one can install the required dependencies using pip.

pip install windows-curses

The only requirement to use the program is a terminal (console); the program will not work using an IDE such as Thonny. For a Hello, world! using curses, one can create the following program:

import curses

def main(screen):
    screen.clear()

    screen.addstr(12, 30, "Hello, my name is Franco!", curses.A_REVERSE)
    screen.addstr(curses.LINES - 1, 0, f"The screen has {curses.LINES - 1} lines and {curses.COLS - 1} columns.")

    screen.refresh()
    screen.getkey()

curses.wrapper(main)

The window or screen is the terminal region that can be drawn. In the code:

  • wrapper() is a feature provided by the Python implementation to initialize the library with greater ease;
  • clear() clears the screen;
  • addstr() writes a string started in the line of the first parameter, column of the second;
  • LINES provides the number of lines of the window and COLS provide the number of columns;
  • refresh() refreshes the screen, redrawing the interface;
  • getkey() blocks the end of program, because it waits for the input of a key. As soon as any key is pressed (it is not necessary to press enter), the program will end. This approach is known as cbreak mode.

Colors and Animations with ncurses

The library provide additional features for drawing in a terminal. For instance, one can draw lines or use colors. Colors can be defined as pairs in init_pair() and chosen using color_pair().

import curses
import time

def main(screen):
    curses.curs_set(0)
    # White text, red background.
    curses.init_pair(1, curses.COLOR_WHITE, curses.COLOR_RED)

    line = 0
    column = 0
    wait_time_seconds = 0.3
    repetitions = 100
    message = "Franco"
    for i in range(repetitions):
        screen.clear()
        screen.addstr(line, column, message, curses.color_pair(1))
        screen.refresh()

        time.sleep(wait_time_seconds)

        # Update the positions to create an animation.
        lines, columns = screen.getmaxyx()

        line += 1
        if (line >= lines):
            line = 0

        column += 1
        if (column >= columns - len(message)):
            column = 0

    screen.getkey()

curses.wrapper(main)

As it has been done for Conway's Game of Life, one can create animations by clearing the screen and redrawing it (after changing values). Instead of a matrix, she/he can think of the screen as a matrix. By modifying positions, the message will change position every time it is drawn.

Input With ncurses

Both getkey() and getch() can be used to read keyboard keys. getkey() returns a string representing the value that has been read. getch() returns an integer with the code of the key.

By selecting one of the two functions, one can change the last example to control the text of message. To do this, it suffices to compare the obtained value with a possible expected value, incrementing or decrementing the position as needed.

import curses
import time

def main(screen):
    curses.curs_set(0)
    # White text, red background.
    curses.init_pair(1, curses.COLOR_WHITE, curses.COLOR_RED)

    line = 0
    column = 0
    wait_time_seconds = 0.1
    end = False
    message = "Franco"
    while (not end):
        screen.clear()
        screen.addstr(line, column, message, curses.color_pair(1))
        screen.refresh()

        time.sleep(wait_time_seconds)

        # Update the position to create an animation.
        key = screen.getch()

        line_increment = 0
        column_increment = 0
        if (key == curses.KEY_UP):
            line_increment -= 1
        elif (key == curses.KEY_DOWN):
            line_increment += 1
        elif (key == curses.KEY_LEFT):
            column_increment -= 1
        elif (key == curses.KEY_RIGHT):
            column_increment += 1
        elif (chr(key) == "q"):
            end = True

        lines, columns = screen.getmaxyx()

        line += line_increment
        if (line >= lines):
            line = lines - 1
        elif (line < 0):
            line = 0

        column += column_increment
        if (column >= columns - len(message)):
            column = columns - len(message) - 1
        elif (column < 0):
            column = 0

        if (message == "Franco"):
            message = "FRANCO"
        else:
            message = "Franco"

curses.wrapper(main)

In the updated example, the input is blocking. In other words, the execution waits a key to be pressed. If one press any of the four arrows in the keyboard (up, down, left or right), the text of message is moved to the selected direction. If she/he press q, the program will end the loop to finish.

Non-Blocking Input With ncurses

It is possible to use nodelay() to switch no non-blocking input. With this setting enabled, getch() returns -1 if it does not read any value.

import curses
import time

def main(screen):
    curses.curs_set(0)
    # White text, red background.
    curses.init_pair(1, curses.COLOR_WHITE, curses.COLOR_RED)

    screen.nodelay(True)
    line = 0
    column = 0
    wait_time_seconds = 0.1
    end = False
    message = "Franco"
    while (not end):
        screen.clear()
        screen.addstr(line, column, message, curses.color_pair(1))
        screen.refresh()

        time.sleep(wait_time_seconds)

        # Update the position to create an animation.
        key = screen.getch()

        line_increment = 0
        column_increment = 0
        if (key != -1):            if (key == curses.KEY_UP):
                line_increment -= 1
            elif (key == curses.KEY_DOWN):
                line_increment += 1
            elif (key == curses.KEY_LEFT):
                column_increment -= 1
            elif (key == curses.KEY_RIGHT):
                column_increment += 1
            elif (chr(key) == "q"):
                end = True

        lines, columns = screen.getmaxyx()

        line += line_increment
        if (line >= lines):
            line = lines - 1
        elif (line < 0):
            line = 0

        column += column_increment
        if (column >= columns - len(message)):
            column = columns - len(message) - 1
        elif (column < 0):
            column = 0

        if (message == "Franco"):
            message = "FRANCO"
        else:
            message = "Franco"

curses.wrapper(main)

With both changes, it is possible to create the first interactive simulation. The loop continues running even if the user does not press a key. In fact, the message of the example alternatives between Franco and FRANCO each step of the loop, with or without input.

Real-time interactive systems work this way. In other words, now it is possible to start creating real-time digital simulations and games for terminals (or consoles). The restriction of creating turn-base interactive games or real-time non-interactive simulations due to the block that was imposed by data input is gone. With a library such as ncurses, one can create interactive simulations and games with the concepts that have been learned hitherto.

Text Area with ncurses

The module for Python also provides a text area for text input, described in the tutorial. The code of the tutorial is adapted in the next example, that defines a text area inside the created window.

import curses
import curses.textpad

import locale
locale.setlocale(locale.LC_ALL, "") # "pt_BR.utf8"
code = locale.getpreferredencoding()

def validator(key):
    return key

def main(screen):
    instructions = [
            "Instructions:",
            "- Ctrl G: quit",
            "- Ctrl H: erase the last character",
            "- Ctrl L: update the screen",
    ]
    instructions_lines = len(instructions)
    line = 0
    for instruction in instructions:
        screen.addstr(line, 0, instruction)
        line += 1

    text_area_lines = curses.LINES - 6
    text_line_columns = curses.COLS - 4
    area_text = curses.newwin(text_area_lines, text_line_columns, instructions_lines + 1, 1)
    curses.textpad.rectangle(screen, instructions_lines, 0, text_area_lines + 5, text_line_columns + 2)
    screen.refresh()

    box = curses.textpad.Textbox(area_text)
    box.edit(validator)
    message = box.gather()

    # Shown after pressing Ctrl G.
    screen.clear()
    screen.addstr(0, 0, message)
    screen.refresh()

    screen.getkey()

curses.wrapper(main)

The area for text input has a limitation. An inspection of the source code reveals that the editor only accepts ASCII encoded characters. The next subsection illustrates a way to show the corresponding non-accented character.

Replacing UTF-8 Characters by Unaccented ASCII Ones

As a curiosity, with some creativity and knowledge about UTF-8 encoding, one can process the values received as input to remove the accents. This allows writing non-accented characters instead of the received ones in the Textbox.

import curses
import curses.textpad
import struct

import locale
locale.setlocale(locale.LC_ALL, "") # "pt_BR.utf8"
code = locale.getpreferredencoding()

REMOVE_ACCENTS = {
    "á": ord("a"),
    "à": ord("a"),
    "ã": ord("a"),
    "é": ord("e"),
    "ê": ord("e"),
    "í": ord("i"),
    "ó": ord("o"),
    "ò": ord("o"),
    "ô": ord("o"),
    "õ": ord("o"),
    "ü": ord("u"),
    "ç": ord("c"),
    "Á": ord("A"),
    "À": ord("A"),
    "Ã": ord("A"),
    "É": ord("E"),
    "Ê": ord("E"),
    "Í": ord("I"),
    "Ó": ord("O"),
    "Ò": ord("O"),
    "Ô": ord("O"),
    "Õ": ord("O"),
    "Ü": ord("U"),
    "Ç": ord("C"),
}

non_ascii_character = []

def validator(key):
    if (key >= 128):
        # Value does not belong to the ASCII table.
        global non_ascii_character
        non_ascii_character.append(key)
        if (len(non_ascii_character) == 1):
            return None
        else:
            # <H is an unsigned short (2 bytes integer) in little-endian order.
            utf8_value = struct.pack("<H", non_ascii_character[1] * 256 + non_ascii_character[0]).decode("utf8")
            non_ascii_character = []

            if (utf8_value in REMOVE_ACCENTS):
                return REMOVE_ACCENTS[utf8_value]
            else:
                # NOTE Ideally, the omission should be recorded, to make it
                # easier to insert missing values to the table.
                return None

    return key

def main(screen):
    instructions = [
            "Instructions:",
            "- Ctrl G: quit",
            "- Ctrl H: erase the last character",
            "- Ctrl L: update the screen",
    ]
    instructions_lines = len(instructions)
    line = 0
    for instruction in instructions:
        screen.addstr(line, 0, instruction)
        line += 1

    text_area_lines = curses.LINES - 6
    text_line_columns = curses.COLS - 4
    area_text = curses.newwin(text_area_lines, text_line_columns, instructions_lines + 1, 1)
    curses.textpad.rectangle(screen, instructions_lines, 0, text_area_lines + 5, text_line_columns + 2)
    screen.refresh()

    box = curses.textpad.Textbox(area_text)
    box.edit(validator)
    message = box.gather()

    # Shown after pressing Ctrl G.
    screen.clear()
    screen.addstr(0, 0, message)
    screen.refresh()

    screen.getkey()

curses.wrapper(main)

The list of accented characters is not completed, though it serves as an example. The principle of the solution is to find the values that were read from the keyboard as bytes, and build the UTF-8 character corresponding to the sequence of bytes. Next, the value is converted to an unaccented ASCII value.

In rigor, a UTF-8 character can have up to four bytes. The current solution works only for characters up to two bytes. To make the solution applicable for any character, one should consider initial values and continuation ones. If she/he wishes, she/he could also avoid using a global variable.

However, the best way to solve the problem consists of creating a custom implementation for a text field or modify the original code of curses.textpad.Textbox. Instead of trying to work around the problem, the ideal approach consists of fixing the original problem with a clean solution.

Albeit a work around can solve a problem, it often creates others. In the example, the modification compromises the feature of removing the last character. Although it is possible to handle the case, it would be better to fix code of Textbox. For example, curses has methods to work with wide char, that would support accents.

Regardless, the example serves as a proof of concept. When one knows programming fundamentals, she/he can create alternatives to overcome the limitations of libraries by exploring the fundamental definitions and concepts.

Exporting Your Program to the PATH

To use your command line programs with greater convenience, that is, without needing to change directory or use the absolute path to the script file, you can create shell scripts. A shell script is a code file to used with command line interpreter. The topic Program Setup provides instructions to configure the PATH.

A practical way to export shell scripts consists of creating a directory for your own scripts. Next, you add that directory to the PATH. For instance, the directory for command line programs written in Python can be C:\Users\Franco\Scripts. This directory would be added to the PATH. The directory does not have to be the same one that stores the source code (the source could directory could be C:\Users\Franco\Python\).

The next step is creating the shell script. In Windows, one can create a shell script using the .bat extension as follows:

@ECHO OFF

REM Replace with the desired directory.
set PATH="C:\Program Files\Python39";%PATH%
python "C:\Users\Franco\Python\script.py"

Next, one can double-click the .bat file to run the program. If one adds the directory with the script to the PATH, she/he would be able to use the command script_name.bat to run the program from any directory in a command line interpreter.

The same process can be performed on Linux and macOS. For instance, on Linux, one can create a .sh script:

#!/bin/bash

# Replace with the desired directory.
export PATH="/home/franco/bin/:$PATH"
python "/home/franco/Python/script.py"

On Linux system, programs that were installed using package managers likely are already available in the PATH. For a more generic example, the previous script assume that the required program is stored in /home/franco/bin/.

With the previous steps, your script becomes a system command. To make it more sophisticated, it is possible to pass command line arguments to the program. This can vary according to the chosen command line interpreter.

If you are interested in such application, try searching how to do it. The next subsection provides an example for Linux and Windows. The Linux version will probably work in macOS, assuming that you use BASH as the command line interpreter.

Exemple: Bunnysay

In Linux systems using BASH, one can use the example of text spoken by the bunny to create the bunnysay program (in analogy to cowsay). The example uses the Lua version. The first step consists of creating a file named bunnysay.sh.

#!/bin/bash

lua "/home/franco/Lua/script.lua" "$@"

The example assumes that the code of the program is called script.lua, which is stored in /home/franco/Lua/. The parameter $@ sends the values received by the shell script as command line arguments to the lua interpreter.

Next, one should add execution permissions to the file:

chmod u+x bunnysay.sh

The previous use of chmod adds the running permission for the current user account to the file bunnysay.sh. If one does not provide the permission, she/he cannot run the file as a program.

The program is ready to be used. If one wishes to make it available in thePATH, she/he can add the directory storing the shell script to the PATH. Otherwise, she/he can export the directory with the shell script locally to a command interpreter session.

export PATH="/home/franco/Lua/:$PATH"
bunnysay.sh "Hello, my name is Franco\!"
date | bunnysay.sh --silent
uname-a | bunnysay.sh --silent

The result is a program that is accessible in the system (or, in the previous example, inside a terminal session). If one wishes to write only bunnysay instead of bunnysay.sh, she/he rename the original file or create a symbolic link as a shortcut.

Bunnysay on Windows

The Windows version is similar. In this case, one creates a file such as bunnysay.bat.

@ECHO OFF

set PATH="C:\Users\Franco\Desktop\lua";%PATH%
lua54.exe "C:\Users\Franco\script.lua" %*

The previous example assumes that the Lua interpreter is in the file lua54.exe, store in the directory lua in the Desktop of the Franco user account. The code of the program is in personal directory of the same user.

In the script, one can write either lua54.exe or lua54; if the extension is omitted, the Windows search for both possibilities. The parameter %* passes all arguments received by the script bunnysay.bat to the program written in Lua.

The way to use the program varies according to the command line interpreter. Assuming that the interpreter is in the directory of the program:

  • In cmd, one can use bunnysay.bat or bunnysay;
  • In PowerShell, on can use .\bunnysay.bat or \.bunnysay.

Parameters are passed as previously.

As it has happened on Linux, one can add the program's directory to the PATH. Alternatively, she/he can export the directory as a part of another script, or call bunnysay.bat using its absolute path as part of a new script. The last option can be interesting because it allows running the created script directly. The next example assumes that bunnysay.bat is on the Desktop of user account Franco.

@ECHO OFF

call "C:\Users\Franco\Desktop\bunnysay.bat" --silent "Hello, my name is Franco!"
pause

After the previous code is saved in a file such as test.bat, one can double-click the script to run it. The result will be displayed in the window that opens. In the implementation, call runs the script's code. pause avoids that the window is closed after the script runs, by waiting the input of a key.

New Items for Your Inventory

Tools:

  • Command line interpreters;
  • Pipe in console (terminal);
  • Input and output redirection;
  • Your own shell scripts and programs.

Skills:

  • Passage of parameters for command line programs;
  • Handling input received as command line arguments;
  • Creation of real-time simulations.

Concepts:

  • Interactive input;
  • Non-interactive input;
  • Pipe;
  • Graphical User Interface (GUI);
  • Text-based User Interface (TUI);
  • Command-line interface (CLI);
  • Unix philosophy;
  • Web request;
  • URL parameters;
  • Real-time interactive system;
  • Blocking input;
  • Non-blocking input.

Programming resources:

  • Use of command line arguments;
  • Shell script;
  • Non-blocking input.

Practice

  1. Write a simple calculator for the command line. It must receive three values from the command line:

    1. The first value for the calculation;
    2. The operator (for instance, +, -, * and / for the basic four arithmetic operations);
    3. The second value for the calculation.

    A usage example is calculator 1 + 1, with 2 as the expected output.

    Tip. Values should be converted to numbers. The arguments will be received as strings.

  2. Enhance your calculator. If it is used as calculator, request the necessary input from the standard input (stdin). If a user provides one or two values, only request the missing ones. If she/he provides more values, perform the calculation with all values. For instance, calculator 1 + 2 + 3 + 4 should provide 10 as the result. Define priority precedence rules.

  3. Use a pipe to combine operations of your calculator.

  4. Create a command line journal. The journal should store the received values in a file. Options of usage parameters can include:

    • -f or --file: the file that will store the value provided by the remainder of the command. If it is omitted, you can use a default file;
    • -n or --new: adds a message to the journal;
    • -l or --last: shows the last added message (for instance, it can be the last line of file);
    • -a or --all: shows all entries in the journal.
  5. A traditional command line program is called fortune. The program works as a fortune cookie simulator. When one uses the fortune command, it shows a random message. Furthermore, the program has parameters that allow filtering messages by category (to learn about some of them, you can consult the manual).

    Create your own version of fortune with some categories.

  6. Use the example of the bunny to write ASCII Art phrases with the output of one the previous programs (or any other of your preference). For instance, you can create the "fortune bunny".

  7. Try to create a shell script for one do the command line programs that you have created.

Next Steps

This topic about command line parameters is unlikely to among the most popular on this website. However, it will certainly reward the people who take the time to learn it. In a fashion, CLI programs are like programming libraries for the operating system. In potential, each new program that one learns to use in the command line adds to all others that she/he already knows. This can allow solving complex tasks as combinations of simpler commands.

Many people associate the command line to the Linux operating system. In fact, Linux has excellent command line interpreters and resources. If you wish to try Linux, you can choose one distribution from the ones suggested below:

However, every modern operating system for desktops provide access to a command line. In particular, the author prefers working with GNU tools even on non-Linux systems. For instance, recent versions of Windows make it easier to use a Linux environment (inside Windows) using the Windows Subsystem for Linux. This way, you can use a Linux environment inside Windows. The installation of git for Windows also provide a command line environment with tools that are compatible or equivalent to the ones provided by the GNU project.

Regardless, many programming languages still have an unexplored feature: bitwise operations. Although they are seldom used in high-level programming, they are useful for low-level programming. Therefore, it is worth knowing them.

  1. Introduction;
  2. Entry point and program structure;
  3. Output (for console or terminal);
  4. Data types;
  5. Variables and constants;
  6. Input (for console or terminal);
  7. Arithmetic and basic Mathematics;
  8. Relational operations and comparisons;
  9. Logic operations and Boolean Algebra;
  10. Conditional (or selection) structures;
  11. Subroutines: functions and procedures;
  12. Repetition structures (or loops);
  13. Arrays, collections and data structures;
  14. Records (structs);
  15. Files and serialization (marshalling);
  16. Libraries;
  17. Command line input;
  18. Bitwise operations;
  19. Tests and debugging.
  • Informatics
  • Programming
  • Beginner
  • Computational Thinking
  • Learn Programming
  • Python
  • Lua
  • Javascript
  • Godot
  • Gdscript
  • Cute