# Custom shell instructions explanations

Custom {{shell_instruction}}s can be set up like described in the
[custom shell instructions how-to](../how-to-guides/basics/custom_shell_instructions_how_to.md).

Here is a few guidelines to follow when creating new {{shell_instruction}}s:

* Don't specify complete path to the programs you want to use.
  E.g. if you want to retrieve the cumulated CPU usage (over all CPU cores) of Java,
  then use a {{shell_instruction}} like this:

    ```{code}
    $ ps -eo %cpu,args | grep java | awk '{print $1}' | paste -sd+ - | bc
    ```

    And not like this:

    ```{code} bash
    $ /path/to/ps -eo %cpu,args | /path/to/grep java | /path/to/awk '{print $1}' | /path/to/paste -sd+ - | /path/to/bc
    ```

    Unless you specify the paths of the symlinked programs
    that have being defined for your restricted bash environment
    (like described in the [security how-to](../how-to-guides/basics/security_how_to.md)),
    e.g. like this:

    ```{code} bash
    $ /home/sshmonitor-user/bin/ps -eo %cpu,args | /home/sshmonitor-user/bin/grep java | /home/sshmonitor-user/bin/awk '{print $1}' | /home/sshmonitor-user/bin/paste -sd+ - | /path/to/bc
    ```

* The default maximum size of a {{shell_instruction}} is 1024 characters.
  This size can be changed in the {{substitutions}} file
  like described in the [`.cmd` and `.substitutions` how-to](../how-to-guides/basics/cmd_and_sub_how_to.md)
  and detailed in the [`.cmd`, `.template` and `.substitutions` explanations](../explanations/cmd_and_template_and_sub_explanations.md)
  thanks to the `<TYPE>_<ID>_INSTRUCTION_LENGTH` {{macro}}
  (where `<TYPE>` is either `SCALAR` or `STRING`,
  and `<ID>` is integer ranging between 1 to 15 for scalars, and between 1 to 5 for strings).

* A {{shell_instruction}} can be suffixed with the `#DEBUG` comment,
  in order to print the {{shell_instruction}} in the {{IOC_shell}} before being send through {{SSH}}.
  Like described in the [`.cmd` and `.substitutions` how-to](../how-to-guides/basics/cmd_and_sub_how_to.md)
  and detailed in the [`.cmd`, `.template` and `.substitutions` explanations](../explanations/cmd_and_template_and_sub_explanations.md).

* A {{shell_instruction}} can be suffixed with the `#LOCAL` comment,
  in order to execute the {{shell_instruction}} on the {{host}} and not on a {{target}}.
  Like described in the [`.cmd` and `.substitutions` how-to](../how-to-guides/basics/cmd_and_sub_how_to.md)
  and detailed in the [`.cmd`, `.template` and `.substitutions` explanations](../explanations/cmd_and_template_and_sub_explanations.md).

* Note that a {{shell_instruction}} can be suffixed with `#LOCAL+DEBUG` or `#DEBUG+LOCAL` if both
  functionality are needed at the same time (e.g. see `STRING_2_INSTRUCTION` in the
  [`.cmd` and `.substitutions` how-to](../how-to-guides/basics/cmd_and_sub_how_to.md)).

* As much as possible, tests your {{shell_instruction}}s directly on the {{target}}, or in an environment that
  is similar enough to be truly representative of the {{target}}'s environment. This is quite important
  because the same program on two different machines can have different outputs (e.g. the version
  of the program may vary, the program could be aliased to a different one, etc). See the bellow
  side note illustrating why this is so important for BusyBox environments.

---

## BusyBox side note

Note that some Linux {{target}}s do not use the GNU Coreutils {{shell_instruction}}s,
but the alternative BusyBox Coreutils {{shell_instruction}}s instead
(this is often the case for embedded Linux {{target}}s).

BusyBox do not implement all the expected options of the Coreutils {{shell_instruction}}s
(the most "uncommon" ones are ignored in order to save space,
which can be interesting for some embedded systems).

E.g. the `free` {{shell_instruction}} with BusyBox Coreutils:

```{code} bash
$ free --help
    BusyBox v1.29.3 (2021-11-19 10:25:20 CET) multi-call binary.
    
    Usage: free [-b/k/m/g]
    
    Display the amount of free and used system memory

$ free
                 total       used       free     shared    buffers     cached
    Mem:       8178052    2559532    5618520       1284      37900      41468
    -/+ buffers/cache:    2480164    5697888
    Swap:            0          0          0
```

Vs the `free` {{shell_instruction}} with GNU Coreutils:

```{code} bash
$ free --help
    Usage:
     free [options]
    
    Options:
     -b, --bytes         show output in bytes
         --kilo          show output in kilobytes
         --mega          show output in megabytes
         --giga          show output in gigabytes
         --tera          show output in terabytes
         --peta          show output in petabytes
     -k, --kibi          show output in kibibytes
     -m, --mebi          show output in mebibytes
     -g, --gibi          show output in gibibytes
         --tebi          show output in tebibytes
         --pebi          show output in pebibytes
     -h, --human         show human-readable output
         --si            use powers of 1000 not 1024
     -l, --lohi          show detailed low and high memory statistics
     -t, --total         show total for RAM + swap
     -s N, --seconds N   repeat printing every N seconds
     -c N, --count N     repeat printing N times, then exit
     -w, --wide          wide output
    
         --help     display this help and exit
     -V, --version  output version information and exit
    
    For more details see free(1).

$ free
                   total        used        free      shared  buff/cache   available
    Mem:        32731448     3269368    27681968      468996     1780112    28596924
    Swap:              0           0           0
```

One can see that a lot of options are missing on the BusyBox version of `free`, and the output is
different.

So, be careful when designing customs {{shell_instruction}}s:
make sure to test them on your {{target}} before using them in your {{substitutions}} file.

If you do not have access to a BusyBox {{target}},
you can install the `busybox` package (see <https://repology.org/project/busybox/versions>),
and locally test your {{shell_instruction}}.
E.g. in order to retrieve available RAM, instead of running :

```{code} bash
$ free -m | grep 'Mem:' | awk '{print $7}'
```

You would rather run:

```{code} bash
$ busybox free -m | busybox grep 'Mem:' | busybox awk '{print $7}'
```

```{warning}
Different versions of BusyBox might differ regarding their output format and/or the accepted options.
E.g. BusyBox `v1.34.1` will accept the `df -i` {{shell_instruction}},
but `v1.29.3` won't.
This might also be the case when comparing very old GNU Coreutils {{shell_instruction}}s and more recent ones.
So, when monitoring an old and/or uncommon {{target}}, as a rule of thumb:
try to test your custom {{shell_instruction}} directly on that {{target}}!
```

```{seealso}
See <https://linuxhandbook.com/what-is-busybox/> for more details about BusyBox.
```
