A Little Story About the 'no' Command
Tags: elixir, programming
Published: 2017-11-22 05:05:39

---
title: A Little Story About the 'no' Command
published: true
description: 
cover_image: https://thepracticaldev.s3.amazonaws.com/i/so0s142c8457mgt54s8w.png
tags: #elixir, #programming
---

Inspired by this article, I made similar commands as Elixir exercises.

http://postd.cc/a-little-story-about-the-yes-unix-command/


## This is the 'no' command to deny all of the world!!!

The specification is almost same as `yes`. Continue to output `n` to the standard output forever.

It makes it possible to use it like this.

```
> no
n
n
n
n
n
n
n
...
```

Alternatively,

```
> no | sh work_a_lot.sh
```

Your annoying boss said, "Work harder!! Work harder!!!"

OMG...It's the way to 'KAROSHI'.

This command should be useful at such times :D

# Let's start

### Environment
```
> elixir -v
[Async - threads: 10] [hipe] [kernel - poll: kernel - poll: kernel - poll: false] [dtrace]

Elixir 1.5.2
```

### Project creation

```
> mix new no
> cd no
```

### Edit `mix.exs`

To create an executable file with Elixir, you can use `escript`.

http://erlang.org/doc/man/escript.html

* `line: 9` Specify module to use `escript`
* `line: 22-24` It is better to write a function that returns the module name etc.

`mix.exs`
```elixir
defmodule No.Mixfile do
  use Mix.Project

  def project do
    [
      app: :no,
      version: "0.1.0",
      elixir: "~> 1.5",
      escript: escript_config(),
      start_permanent: Mix.env == :prod,
      deps: deps()
    ]
  end

  # Run "mix help compile.app" to learn about applications.
  def application do
    [
      extra_applications: [:logger]
    ]
  end

  defp escript_config do
    [main_module: No]
  end

  # Run "mix help deps" to learn about dependencies.
  defp deps do
    [
      # {:dep_from_hexpm, "~> 0.3.0"},
      # {:dep_from_git, git: "https://github.com/elixir-lang/my_dep.git", tag: "0.1.0"},
    ]
  end
end
```

`No.main/1` of the module specified here becomes the entry point.

## Implementation

It's not surprising that it just loops normally.I added an option `-p` that generates a lot of processes and keeps outputting `n` for each.Also, I made it possible to see the PID with `-d` so that I could observe the parallel execution.

`OptionParser` Conveniently useful!

`no.exs`
```elixir
defmodule No do
  @moduledoc """
  Documentation for No.
  """

  def main(args) do
    args
    |> parse_args()
    |> run()
    |> wait()
  end

  def say(args) do
    {argv, process_num, sender, debug} = args
    case debug do
      true -> send sender, {:debug, "#{expletive(argv)} from process no.#{process_num}", self()}
      _    -> send sender, {:ok, expletive(argv)}
    end
    say(args)
  end

  defp expletive(_ = ''), do: 'n'
  defp expletive(value), do: value

  defp parse_args(args) do
    {opts, argv, _} = OptionParser.parse(
      args,
      swithces: options_list(),
      aliases: aliases_list()
    )
    {argv, opts}
  end

  defp options_list() do
    [
      processes: :integer,
      debug: :boolean,
    ]
  end

  defp aliases_list() do
    [
      p: :processes,
      d: :debug,
    ]
  end

  defp run(args) do
    {argv, opts} = args
    num = case opts[:processes] do
      nil -> 1
      _  -> String.to_integer(opts[:processes])
    end
    (1..num)
    |> Enum.each(
      fn(n) ->
        spawn_link(No, :say, [{argv, n, self(), opts[:debug]}])
      end
    )
  end

  defp wait(args) do
    receive do
      {:ok, message} ->
        IO.puts message
      {:debug, message, pid} ->
        IO.inspect {message, pid}
    end
    wait(args)
  end
end
```

### Build

When you have finished implementing build.
Run in a directory with `mix.exs`.

```
> mix escript.build
```

Executable file should be generated in the current directory.

## Execution

In the current directory.

```
> ./no -p 100
n
n
n
n
n
...
```

Debug option available.

```
>.. / no - p 100 - d
{"n from process no. 47", # PID <0.122.0>}
{"n from process no. 47", # PID <0.122.0>}
{"n from process no. 47", # PID <0.122.0>}
{"n from process no. 40", # PID <0.115.0>}
{"n from process no. 47", # PID <0.122.0>}
...
```

Gotcha!!!!

# How fast is it?

```
> ./no | pv -r > /dev/null
[93.7 KiB / s]
```

As for the `yes` command,
```
> yes | pv -r > /dev/null
[31.6 MiB / s]
```

`no` command is toooo slow!!!! xD
Calm down, I believe it will reverse in parallel processing!!

```
>./no - p 100 | pv -r > /dev/null
[1.12 KiB / s]
```

...Why?

## Summary

* It is good to reimplement the Linux command as a language practice subject
* Many of the functions are reasonably sized
* It is pleasant to go through processing with * `|>`
* I don't know why performance is slow.
* I will investigate later.
* Registration to Hex
* Registered -> http://blog.tokoyax.com/entry/elixir/register-to-hex

Github Repo: https://github.com/tokoyax/no

This article is translated from Japanese origin.

http://blog.tokoyax.com/entry/elixir/no-command


(reference)
* https://github.com/rizafahmi/elixirdose-cli
* https://dev.to/roperzh/overview-of-elixir-executables-4bk
* https://qiita.com/niku/items/729ece76d78057b58271
* https://qiita.com/niku/items/5b7967d88cae58e034e2