documenting shell scripts' parameters
Asked Answered
F

8

67

Is there a convention for documenting shell scripts' parameters?

For example:

#!/usr/bin/env bash

# <description>
#
# Usage:
#  $ ./myScript param1 [param2]
# * param1: <description>
# * param2: <description>

A few things I don't like about this particular template:

  • the script's file name (myScript) appears within the file itself
  • parameter description seems weird
  • the leading space before $ is visually useful, but can lead to confusion in languages with block comments, causing some validation tools to complain about mixed/inconsisent indentation (e.g. spaces in this block, tabs for code - provided one prefers tabs, of course)

Are there any guidelines on this?

Flap answered 26/3, 2009 at 22:21 Comment(1)
man pages for formatting and examples of parameter documentation: unix.stackexchange.com/questions/6891/…Totipalmate
A
69

Traditionally you document your arguments in the usage() function:

#!/bin/bash

programname=$0

function usage {
    echo "usage: $programname [-abch] [-f infile] [-o outfile]"
    echo "  -a      turn on feature a"
    echo "  -b      turn on feature b"
    echo "  -c      turn on feature c"
    echo "  -h      display help"
    echo "  -f infile   specify input file infile"
    echo "  -o outfile  specify output file outfile"
    exit 1
}

usage
Alemanni answered 26/3, 2009 at 22:34 Comment(6)
Thanks to everyone for their responses. While they are all fairly Bash-specific, they're useful nevertheless. I'll have to think about how best to implement this (as a common pattern) in Python, Perl, Ruby etc.Flap
Having thought about this a little more, the in-code documentation is required as well, as it serves a slightly different purpose. So I will adopt the automated usage info, but would still appreciate some advice on the original issue.Flap
I have a question. Why should you use exit code 1 here?Pageantry
@Pageantry Because if you're printing usage, you most likely did not complete the function of the program, so you return failure. A great example is if not supplying any arguments causes the script to call usage, and another script called this script, intending to supply arguments, but not doing so due to a bug, it's ideal for this script to communicate to the calling script that it failed to do anything.Cointreau
@DuncanXSimpson What if the arguments-supplier program is intended to print usage at some point? It is probably better to call exit 1 only in handled unexpected situations rather than in the quite expected situation of asking for help. I would say that printing self usage is part of the function of a program. Also, I'd expect a usage function to just print usage, not to terminate the program, so exit should be called outside usage.Thumbtack
How do I call the usage function?Mooncalf
B
35

I would recomment using a heredoc:

usage () {
    cat <<HELP_USAGE

    $0  [-a] -f <file>

   -a  All the instances.
   -f  File to write all the log lines
HELP_USAGE
}

instead of:

echo "$0  [-a] -f <file>"
echo
echo "-a  All the instances."
echo "-f  File to write all the log lines."

I think it is way cleaner than all these echo lines.

Baklava answered 14/11, 2016 at 0:39 Comment(3)
I think this solution is the most beautiful of all the answers. ThanksHy
I avoid heredoc only because it destroys indentation.Khedive
@DávidHorváth You may indent the heredoc contents by adding a "-" to the stop token : tldp.org/LDP/abs/html/here-docs.html#LIMITSTRDASHUnchain
R
33

I usually wrap my usage in function so I can call it from a -h param etc.

#!/bin/bash
usage() {
    cat <<EOM
    Usage:
    $(basename $0) Explain options here

EOM
    exit 0
}

[ -z $1 ] && { usage; }
Rosalia answered 26/3, 2009 at 22:33 Comment(4)
I fixed the here doc for you by indenting the script. I don't understand the [ -x $1 ] (if the first argument isn't the path to an executable file, produce usage?); the braces and semicolon around the invocation are redundant too.Makedamakefast
Doh, didn't notice the x; changed it to the z it wanted to be.Rosalia
I guess the braces are habit so I can include an error message along with the check depending on the check. Thanks for the indenting code trick!Rosalia
You need to quote that $1 since it could contain spaces and [ is just a regular program that takes -z as the first argument, ] last. If $1 contained "x -o 1 -eq 1", it would always show the usage information.Weighin
P
16

The Vim bash IDE that does this:

#!/bin/bash
#===============================================================================
#
#          FILE:  test.sh
#
#         USAGE:  ./test.sh
#
#   DESCRIPTION:
#
#       OPTIONS:  ---
#  REQUIREMENTS:  ---
#          BUGS:  ---
#         NOTES:  ---
#        AUTHOR:  Joe Brockmeier, [email protected]
#       COMPANY:  Dissociated Press
#       VERSION:  1.0
#       CREATED:  05/25/2007 10:31:01 PM MDT
#      REVISION:  ---
#===============================================================================
Picayune answered 26/3, 2009 at 22:28 Comment(3)
Ugh, looks like the identification section of COBOL program. shivers.Illiteracy
That looks interesting - will consider that, thanks! (Though the problem with multi-line comments - as in heredoc - remains.)Flap
I always use "#:" for this meta-comments so I can separate them from the implementation comments.Baklava
D
7

I would recommend making your script automatically print usage (if it shouldn't be run without arguments):

#!/usr/bin/env bash

if [ $# == 0 ]; then
    echo "Usage: $0 param1 [param2]"
    echo "* param1: <description>"
    echo "* param2: <description>"
fi
Doit answered 26/3, 2009 at 22:27 Comment(1)
You can use $# as a shortcut for the argument count.Weighin
A
3

I would rather write:

Usage: `basename $0` [option1]|[option2] param1
  Options:
   - option1:  .....
   - option2:  .....
  Parameters:
   - param1:   ..... 

Try to look at the way help is formatted for standard UNIX utilities (ls --help, for instance)

Atomizer answered 26/3, 2009 at 22:26 Comment(0)
D
0

Based on @eddy's excellent answer here's the version that takes care of multiple arguments (in this example 2)

#!/bin/bash

usage() {
    cat <<EOM
Usage: $(basename $0) <username> <myproject> 
where 
  <username> is your user name
  <myproject> is your project's name
EOM
    exit 1
}

[ $# != 2 ] && { usage; }
for arg in "$@"
do
    [ -z "$arg" ] && { usage; }
done

The double check of the length of the array of arguments ([ $# != 2 ]) as well as on arguments to be not null ([ -z "$arg" ]) is needed to take care of empty strings given as arguments.

David answered 10/10, 2023 at 13:54 Comment(0)
W
0

I tend to use a Doxygen style for file headers:

#!/bin/bash
##
 #  @file       filename.sh
 #  @brief      Brief description
 #
 #>>>Usage
 #? 
 #? $0 [OPTIONS]
 #? 
 #? -?, -h, --help      Display this help and exit
 #? 
 #<<<Usage
 #
 #  @version    2024-07-05T22:40:33
 ##

# Print header starting with Doxygen codes
grep -E '^ *# *@' $0 | cut -f 2- -d '@'

#----------------------------------------------------------------------

function usage() { grep -E '^ *#\? *' $0 | cut -f 2- -d '?' ; exit; }

Makes it simple to extract header:

file       filename.sh
brief      Brief description
version    2024-07-05T22:40:33

and usage separately from the same header.

 $0 [OPTIONS]

 -?, -h, --help      Display this help and exit
Wodge answered 5/7, 2024 at 21:23 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.