1

I have the following function that print NUM lines from the beginning of a set of files.

The command (function, script, whatever) accepts a variable number of arguments, and I would like to reserve one special option for "tell me how to use you", the traditional choices being "help", "-h", "-?" and "--help".

headrc ()
{
    # Prints first set of lines from named files.
    # $1 NUM  Number of lines to print
    # $2 DIR  Directory

    num=$1
    dir=$2
      
    find "$dir" \( -name \*.org -o -name \*.texi \)  \
      | xargs head -n "$num";
}

1 Answer 1

2

You could do it manually, but using getopt would be more useful moving forward. Consider for example:

#!/bin/bash

# Prints first set of lines from named files.
# $1 NUM  Number of lines to print
# $2 DIR  Directory
headrc() {
    eval set -- $(getopt --name "${FUNCNAME[0]}" --options h --longoptions help -- "${@}")

    while [[ "${1}" != "--" ]]; do
        case "${1}" in
        -h | --help)
            printf "Usage: ${FUNCNAME[0]} [-h|--help] <args>\n"
            return 1
            ;;
        *)
            printf "Unknown option: ${1}\n"
            ;;
        esac

        shift # Shift off option
    done
    shift # Shift off --

    local -r num="${1}"
    local -r dir="${2}"

    find "${dir}" \( -name \*.org -o -name \*.texi \) | xargs head -n "${num}";
}

The getopt tool takes the existing arguments (${@}) and the options that you give it, and reorders them such that all the options come first, then a --, then everything else. The --options option specifies the single-character options and the --longoptions specifies the "long" (multi-character) options.

For example:

$ getopt --options fh --longoptions file,help -- a b c -f d e --help g
 -f --help -- 'a' 'b' 'c' 'd' 'e' 'g'

Note that getopt supports options with parameters, but since you don't need that here, I didn't cover that aspect of the tool.

The eval set -- updates the arguments to the given values, rewriting what the function sees as its arguments to the output of getopt.

The while loop processes the options; the options stop when it encounters --. If is see -h or --help, it prints a help message and returns. Each time it processes the loop it uses shift to "shift off" the first argument.

Once it finds the -- it stops looping, and shifts off that argument.

You're now left with everything that came after the options, so you can use the positional arguments like you did before.

3
  • How can I skip the find command if I pass -h ?
    – Pietru
    Jun 29, 2021 at 2:50
  • +1 but it's worth pointing out that only the version of getopt from util-linux is safe to use (this is pretty much the standard/default getopt on linux but not on other systems). Other versions have serious problems (e.g. can't handle empty arguments or args containing whitespace) and should not be used.
    – cas
    Jun 29, 2021 at 3:14
  • @Pietru What I've shown does skip the find if you pass -h; see the return 1. Jun 30, 2021 at 0:22

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.