Bash脚本“用法”输出格式

时间:2013-10-22 02:14:18

标签: bash scripting formatting

输出bash脚本的帮助文本以使列线正确排列的好方法是什么?

类似的东西:

 Usage: mycommand [options]

    -h| --help           this is some help text.
                         this is more help text.
    -1|--first-option    this is my first option
    -2|--second-option   this is my second option

3 个答案:

答案 0 :(得分:22)

我喜欢使用cat

usage.sh

#!/bin/bash

cat <<EOF
Usage: $0 [options]

-h| --help           this is some help text.
                     this is more help text.
-1|--first-option    this is my first option
-2|--second-option   this is my second option
EOF

这将输出:

Usage: usage.sh [options]

-h| --help           this is some help text.
                     this is more help text.
-1|--first-option    this is my first option
-2|--second-option   this is my second option

答案 1 :(得分:2)

Heredocs还有一个标签缩进选项。这允许您使用任意数量的选项卡为每行代码添加前缀 - 这些选项卡将被“吃掉”#34;在输出上,左对齐输出。请注意尾随的EOF&#39; (在这个例子中)必须完全留下缩进 - 'EOF&#39;不能缩进标签。

小心将任何将制表符转换为空格的编辑器(例如&#34; vi&#34;选项是&#34; expandtab&#34;将制表符转换为空格)。不幸的是,多个空间不会被吃掉#34;像标签一样。如果您使用&#39; expandtab&#39; (或类似的选项)代码格式化,然后heredoc选项卡缩进方法可能对您没用。

在下面的示例中,&#34;&lt;&lt; - &#34;是heredoc用于表示选项卡缩进的语法。

示例:

    cat <<-EOF
    usage.sh [options]

    -h| --help           this is some help text.
                         this is more help text.
    -1|--first-option    this is my first option
    -2|--second-option   this is my second option
EOF

请注意,有&#34;标签&#34;在&#34; cat&#34;和后续行的前面 - 这里的HTML格式可能不会允许你剪切粘贴示例。

注意终止&#34; EOF&#34;是合理的。

答案 2 :(得分:1)

我认为这种任务的真正完美解决方案应该比这更复杂。在大多数shell中,环境变量echo ${COLUMNS}可用于在脚本中了解终端窗口的宽度。

我为我编写的脚本创建了一个简单的用法函数,它考虑了${COLUMNS}。在评论中尽可能地解释:

# Put here all the options your script accepts
local options=(
    '-c,--config <FILE>'
    '-l,--list'
    '-r,--run'
    '-v,--verbose'
    '-n,--dry-run'
    '-h,--help'
)
# Put here the corresponding descriptions for every option you specified in the array above
local descriptions=(
    "Use the given configuration file instead of the default one"
    "List all program related files. if used with \`--verbose\`, the full contents are printed"
    "Try to process all the files"
    "Turn on verbose output"
    "don't store files like alwyas but show only what actions would have been taken if $(basename "$0") would have run normally (with or without --run), implies --verbose"
    "display help"
)
# Put here the offset options will get
local options_offset=3
# Put here the offset descriptions will get after the longest option
local descriptions_offset_after_longest_option=5
# Put here the maximum length of descriptions spanning
local maximum_descriptions_length=80

# ---------------------------------
# Up until here is the configuration
# ---------------------------------

# First we print the classic Usage message
echo "Usage: $(basename "$0") [OPTION]..."
# In the next loop, we put in ${max_option_length} the length of the
# longest option. This way, we'll be able to calculate the offset when
# printing long descriptions that should span over several lines.
local max_option_length=1
for (( i = 0; i < ${#options[@]}; i++)); do
    if [[ $max_option_length -lt ${#options[$i]} ]]; then
        max_option_length=${#options[$i]}
    fi
done
# We put in the following variable the total offset of descriptions
# after new-lines.
local descriptions_new_line_offset=$((${max_option_length} + ${options_offset} + ${descriptions_offset_after_longest_option}))
# The next loop is the main loop where we actually print the options with
# the corresponding descriptions.
for (( i = 0; i < ${#options[@]}; i++)); do
    # First, we print the option and the offset we chose above right before it
    printf -- '%*s' ${options_offset}
    printf -- '%s' "${options[$i]}"
    # Here we start tracking through out this loop the current index of the
    # char on the terminal window. This is necessary because in the process
    # of printing the descriptions' words we'll be able to know how not to
    # span over the defined maximum length or not to split words when
    # hitting ${COLUMNS}
    local current_char_index=$((${options_offset} + ${#options[$i]}))
    # We calculate the offset which should be given between the current
    # option and the start of it's description. This is different for every
    # option because every option has a different length but they all must
    # be aligned according to the longest option's length and the offsets
    # we chose above
    local current_description_offset=$((${max_option_length} - ${#options[$i]} + ${descriptions_offset_after_longest_option}))
    # We print this offset before printing the description
    printf -- '%*s' ${current_description_offset}
    # Updating the current_char_index
    current_char_index=$((${current_char_index} + ${current_description_offset}))
    # We put in a temporary variable the current description from the array
    local current_description="${descriptions[$i]}"
    # We divide the current_description to an array with the description's
    # words as the array's elements. This is necessary so we can print the
    # description without spliting words
    IFS=' ' read -r -a description_words <<< "${current_description}"
    # We start a loop for every word in the descriptions words array
    for (( j = 0; j < ${#description_words[@]}; j++)); do
        # We update the current char index before actually printing the
        # next word in the description because of the condition right
        # afterwards
        current_char_index=$((${current_char_index} + ${#description_words[$j]} + 1))
        # We check if the index we will reach will hit the maximum limit we
        # chose in the beginning or the number of ${COLUMNS} our terminal
        # gives us
        if [[ ${current_char_index} -le ${COLUMNS} ]] && [[ ${current_char_index} -le ${maximum_descriptions_length} ]]; then
            # If we don't hit our limit, print the current word
            printf -- '%s ' ${description_words[$j]}
        else
            # If we've hit our limit, print a new line
            printf -- '\n'
            # Print a number of spaces equals to the offset we need to give
            # according to longest option we have and the other offsets we
            # defined above
            printf -- '%*s' ${descriptions_new_line_offset}
            # print the next word in the new line
            printf -- '%s ' ${description_words[$j]}
            # Update the current char index
            current_char_index=$((${descriptions_new_line_offset} + ${#description_words[$j]}))
        fi
    done
    # print a new line between every option and it's description
    printf '\n'
done