【问题标题】:Bash: Keeping indentation during interpolationBash:在插值过程中保持缩进
【发布时间】:2014-07-22 10:20:14
【问题描述】:

我有一个包含多行字符串的变量。

我将把这个变量插入到另一个多行回显字符串中,这个回显字符串有缩进。

这是一个例子:

ip_status=`ip -o addr | awk 'BEGIN {  printf "%-12s %-12s %-12s\n", "INTERFACE", "PROTOCOL", "ADDRESS"
                                      printf "%-12s %-12s %-12s\n", "---------", "--------", "-------" }
                                   {  printf "%-12s %-12s %-12s\n", $2, $3, $4 }'`

echo -e "
    ->
    $ip_status
    ->
"

运行时,$ip_status 的第一行与-> 对齐,但随后的行与-> 不对齐。

如果您在 bash 中运行它会更容易查看。这是输出:

    ->
    INTERFACE    PROTOCOL     ADDRESS
---------    --------     -------
lo           inet         127.0.0.1/8
lo           inet6        ::1/128
eth0         inet         10.0.2.15/24
eth0         inet6        fe80::a00:27ff:fed3:76c/64
    ->

我希望$ip_status 中的所有行都与-> 对齐,而不仅仅是第一行。

【问题讨论】:

  • 您可以使用sed 添加空格,例如:echo "$ip_status" | sed 's/^/ /'

标签: bash


【解决方案1】:

您需要自己插入缩进。尽管有一些可能有用的实用程序(例如,column -t 在这类应用程序中经常有用),但 Bash 没有使文本漂亮的功能。

不过,插入缩进并不太难。这是一个解决方案:

echo "
    ->
    ${ip_status//$'\n'/$'\n    '}
    ->
"

注意:我删除了非标准的 -e 标志,因为它确实没有必要。

另一种选择是使用sed 之类的工具对整个输出应用替换:

echo "
 ->
   $ip_status
   ->
" | sed 's/^ */    /'

第二个可能的优点是它会整理缩进,即使它像示例中那样衣衫褴褛。如果您不想要这种效果,请改用's/^/ /'

或者一个小的 shell 函数,它的第一个参数是所需的缩进,其余的参数缩进并在每个参数之后用换行符连接:

indent() {
  local s=$(printf '%*s' $1 "")
  shift
  printf "$s%s\n" "${@//$'\n'/$'\n'$s}"
}
indent 4 '->' "$ip_status" '->'

这可能需要一些解释:

  • printf 接受 * 作为长度说明符,就像 C 版本一样。它的意思是“使用相应的参数作为数值”。所以local s=$(printf '%*s' $1 "") 创建了一串长度为$1 的空格。

  • 此外,printf 会根据需要重复其格式以使用所有参数。所以第二个printf 在每个参数的开头应用缩进,在结尾应用换行符。

  • "${@/pattern/subst}" 是依次应用于每个参数的替换。在开头使用两个斜杠 ("${@//pattern/subst}") 使其重复替换。

  • $'\n' 是解释 C 样式反斜杠转义的常用语法,由 bash 和各种其他 shell 实现。 (但它在最小的 posix 标准 shell 中不可用。)

  • 所以"${@//$'\n'/$'\n'$s}" 在每个参数的每个换行符之后插入$s——即所需的缩进。

【讨论】:

  • 您的第一个示例似乎不起作用。它不保留缩进。
  • 为什么@后面需要//?为什么它需要 2 个正斜杠,这个转变是为了什么?
  • 哦 shift 本质上是忽略第一个数字。
  • @CMCDragonkai:抱歉,第一个示例中有一个小错字(我写了'$\n' 而不是正确的$'\n')。现在已经修好了。答案包括对${@// 的解释。您可以在 bash 手册中找到更多详细信息:gnu.org/software/bash/manual/…
【解决方案2】:
echo  "    ->"
while IFS= read -r line
do
    echo "    $line"
done <<< "$ip_status"
echo  "    ->"

您可以逐行读取变量,并在其前面加上所需的空格数。我已经使用了这个question 的公认答案。


使其成为一个功能:
myfunction() {
echo  "    ->"
while IFS= read -r line
do
    echo "    $line"
done <<< "$1"
echo  "    ->"
}

myfunction "$ip_status"

【讨论】:

  • 这样的构造应该被包裹在一个函数中吗?
  • @CMCDragonkai,你可以做到,特别是如果你需要不止一次,我已经编辑了我的答案以包含一个函数。
【解决方案3】:

一个简单的形式是使用readarray、进程替换和printf

readarray -t ip_status < <(exec ip -o addr | awk 'BEGIN {  printf "%-12s %-12s %-12s\n", "INTERFACE", "PROTOCOL", "ADDRESS"
                                      printf "%-12s %-12s %-12s\n", "---------", "--------", "-------" }
                                   {  printf "%-12s %-12s %-12s\n", $2, $3, $4 }')

printf '    %s\n' '->' "${ip_status[@]}" '->'

参考:http://www.gnu.org/software/bash/manual/bashref.html

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-12-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-01-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多