输出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
答案 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