如何在Bash中输出多行字符串?

我如何在Bash中输出一个多重字符串,而不使用多个echo调用,像这样:

echo "usage: up [--level <n>| -n <levels>][--help][--version]"
echo
echo "Report bugs to: "
echo "up home page: "

我正在寻找一种可移植的方法来做到这一点,只使用Bash内置程序。

429819 次浏览

使用-e选项,则可以在字符串中打印带有\n的新行字符。

样本(但不确定是否一个好或不好)

有趣的是,-e选项没有记录在MacOS的手册页中,但仍然可用。它在Linux的手册页中有记录。

或者你可以这样做:

echo "usage: up [--level <n>| -n <levels>][--help][--version]


Report bugs to:
up home page: "

在这里,文档通常用于此目的。

cat << EOF
usage: up [--level <n>| -n <levels>][--help][--version]


Report bugs to:
up home page:
EOF

所有boure派生的shell(包括所有版本的Bash)都支持它们。

既然我在评论中推荐了printf,我可能应该给出一些它的用法示例(尽管对于打印用法消息,我更可能使用Dennis或Chris的答案)。printf使用起来比echo复杂一些。它的第一个参数是一个格式字符串,其中转义(如\n)被总是解释;它还可以包含以%开头的格式指令,该指令控制任何附加参数被包含在其中的位置和方式。这里有两种不同的方法来使用它的用法消息:

首先,你可以在格式字符串中包含整个消息:

printf "usage: up [--level <n>| -n <levels>][--help][--version]\n\nReport bugs to: \nup home page: \n"

注意,与echo不同,必须显式地包含最后的换行符。另外,如果消息碰巧包含任何%字符,它们必须被写成%%。如果你想包含bugreport和主页地址,它们可以很自然地添加:

printf "usage: up [--level <n>| -n <levels>][--help][--version]\n\nReport bugs to: %s\nup home page: %s\n" "$bugreport" "$homepage"

其次,你可以使用format字符串让它在单独的行中打印每个额外的参数:

printf "%s\n" "usage: up [--level <n>| -n <levels>][--help][--version]" "" "Report bugs to: " "up home page: "

使用这个选项,添加bugreport和主页地址是相当明显的:

printf "%s\n" "usage: up [--level <n>| -n <levels>][--help][--version]" "" "Report bugs to: $bugreport" "up home page: $homepage"

同样,对于缩进的源代码,您可以使用<<-(带末尾破折号)忽略前导制表符(但不能使用前导空格)。

比如这个:

if [ some test ]; then
cat <<- xx
line1
line2
xx
fi

输出不带前导空格的缩进文本:

line1
line2

受到本页上富有洞察力的回答的启发,我创造了一个混合的方法,我认为这是最简单和更灵活的方法。你怎么看?

首先,我在一个变量中定义了它的用法,这允许我在不同的上下文中重用它。格式非常简单,几乎是所见即所得,不需要添加任何控制字符。对我来说,这似乎是合理的可移植性(我在MacOS和Ubuntu上运行它)

__usage="
Usage: $(basename $0) [OPTIONS]


Options:
-l, --level <n>              Something something something level
-n, --nnnnn <levels>         Something something something n
-h, --help                   Something something something help
-v, --version                Something something something version
"

那么我就可以简单地把它用作

echo "$__usage"

或者更好的是,当解析参数时,我可以在一行中回显它:

levelN=${2:?"--level: n is required!""${__usage}"}

使用-e参数和转义字符\n:

echo -e "This will generate a next line \nThis new line is the result"

还有一件事,使用带有预定义变量(这里:msg)的printf作为模板。

msg="First line %s
Second line %s
Third line %s
"


one='additional message for the first line'
two='2'
tri='this is the last one'


printf "$msg" "$one" "$two" "$tri"

这^^^将按指定顺序打印整条消息,并插入额外的vars,而不是%s

我通常使用内置的read命令,我认为它更灵活和直观。 它将一行内容读入一个变量,并允许与特殊shell变量IFS绑定的分词。 更多细节请参考博客或手册页
read -r -d '' usage <<-EOF
usage: up [--level <n>| -n <levels>][--help][--version]


Report bugs to: $report server
up home page: $HOME
EOF
echo "$usage"
    You can write your
text
freely,
in a separate:
----file.

然后

echo "$(</pathto/your_multiline_file)"

这样做:

dedent() {
local -n reference="$1"
reference="$(echo "$reference" | sed 's/^[[:space:]]*//')"
}


text="this is line one
this is line two
this is line three\n"


# `text` is passed by reference and gets dedented
dedent text


printf "$text"

不先调用dedent输出:

this is line one
this is line two
this is line three

...and WITH首先调用dedent(如上所示):

this is line one
this is line two
this is line three

完整的解释,请看我已经写过的地方:

  1. 相当于python在bash中的textwrap dedent
  2. 带额外空格的多行字符串(保留缩进)

当然,感谢@Andreas Louv的这里显示了函数的sed部分

以下是我的做法:

function help_text {
printf "\n\
Usage: ./cpanel-to-cc.sh [arguments] ... \n\
Examples: \n\
\t ./cpanel-to-cc.sh --client-id 123123 --api-key abc123def456 --domain example.com \n\
\t ./cpanel-to-cc.sh --client-id 123123 --tmp-dir /home/user/cpanel-to-cc \n\
\t ./cpanel-to-cc.sh --resync --domain example.com \n\
\t ./cpanel-to-cc.sh --purge \n\
\n\
Arguments: \n\
Option \t\t\t Long option \t\t\t Function \n\
-c <id> \t\t --client-id <id> \t\t Specify the SiteHost Client ID \n\
-k <key> \t\t --api-key <key> \t\t Specify the SiteHost API key with access to Cloud, Job and Server modules \n\
-d <domain> \t\t --domain <domain> \t\t The cPanel domain to migrate. If not specified we try migrate all \n\
-t <directory> \t --tmp-dir <directory> \t\t Directory to store temporary files and logs. Default is: $TMP_DIR \n\
-v \t\t\t --verbose \t\t\t Print debugging/verbose information \n\
-y \t\t\t --assume-yes \t\t\t Automatic yes to prompts. Assume \"yes\" as answer to all prompts \n\
-r \t\t\t --resync \t\t\t Use credentials stored and copy data into Container already created. \n\
-p \t\t\t --purge \t\t\t Remove any metadata stored on the the server. This removes any files in: $TMP_DIR \n\
-h \t\t\t --help \t\t\t Display this help and exit \n\
\n"
}