string - 内置 printf bash 的可能填充错误

标签 string bash formatting printf padding

在使用内置的 printf bash 将字符串填充到特定宽度时,我遇到了一些“逐一”问题。

采用以下代码:

#!/bin/bash
# vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4:

# Only display motd if tty and not sudoing as root
[ "$PS1" ] && [ "$EUID" -ne 0 ] || return 0

# Run the entire function in its own subshell.
#
# The local keyword in functions prevents inheriting values.
# The subshell prevents exporting them.
#
# Technically, local prevents exporting too. Only the vars that
# could be used before initialized need to be declared local to
# prevent the parent env from leaking into it.
(
    MOTD_DEFAULT_VALUE="-"

    function show_motd() {

        local MOTD_AVAILABILITY_ZONE \
              MOTD_INSTANCE_ID \
              MOTD_INSTANCE_TYPE \
              MOTD_VPC_ID \
              MOTD_PUBLIC_IP

        MOTD_AWS_METADATA_URL="http://169.254.169.254/latest/meta-data"

        # Detect if inside AWS
        MOTD_INTERFACE_PRIMARY_MAC="$(curl -s --connect-timeout 0.1 --max-time 0.1 ${MOTD_AWS_METADATA_URL}/network/interfaces/macs/ 2>/dev/null | sed -n 1p | cut -c-17)"
        if [ -n "${MOTD_INTERFACE_PRIMARY_MAC}" ]; then
            MOTD_AVAILABILITY_ZONE="$(curl -s ${MOTD_AWS_METADATA_URL}/placement/availability-zone 2>/dev/null)"
            MOTD_INSTANCE_ID="$(curl -s ${MOTD_AWS_METADATA_URL}/instance-id 2>/dev/null)"
            MOTD_INSTANCE_TYPE="$(curl -s ${MOTD_AWS_METADATA_URL}/instance-type 2>/dev/null)"

            MOTD_VPC_ID="$(curl -s ${MOTD_AWS_METADATA_URL}/network/interfaces/macs/${MOTD_INTERFACE_PRIMARY_MAC}/vpc-id 2>/dev/null)"
            [[ "${MOTD_VPC_ID}" == *'Not Found'* ]] && MOTD_VPC_ID=""

            MOTD_PUBLIC_IP="$(curl -s ${MOTD_AWS_METADATA_URL}/public-ipv4 2>/dev/null)"
            [[ "${MOTD_PUBLIC_IP}" == *'Not Found'* ]] && MOTD_PUBLIC_IP=""
        fi

        MOTD_OS="$(cat /etc/system-release | sed 's/ release / /g' 2>/dev/null)"
        [ -z "${MOTD_OS}" ] && MOTD_OS="$(cat /etc/os-release | grep 'PRETTY_NAME' | cut -d\" -f2 2>/dev/null)"

        MOTD_HOSTNAME="$(hostnamectl --static 2>/dev/null)"
        if [ -z "${MOTD_HOSTNAME}" ]; then
            MOTD_HOSTNAME="$(hostnamectl --transient 2>/dev/null)"
            if [ -z "${MOTD_HOSTNAME}" ]; then
                MOTD_HOSTNAME="$(hostname 2>/dev/null)"
            fi
        fi

        if [ -z "${MOTD_PUBLIC_IP}" ]; then
            MOTD_PUBLIC_IP="$(ip -4 addr show scope global primary | sed -n 5p | cut -d\  -f6 | cut -d/ -f1 2>/dev/null)"
        fi

        MOTD_GATEWAY_IP="$(curl -s http://checkip.amazonaws.com 2>/dev/null)"
        MOTD_PRIVATE_IP="$(ip -4 addr show scope global primary | sed -n 2p | cut -d\  -f6 | cut -d/ -f1 2>/dev/null)"
        MOTD_TOTAL_CPUS="$(grep processor /proc/cpuinfo | wc -l 2>/dev/null)"
        MOTD_TOTAL_DISKS="$(df -h | grep '^\/dev\/' | wc -l 2>/dev/null)"
        MOTD_TOTAL_DISK_USED="$(df -h | grep '^\/dev/' | sed -n 1p | awk '{print $3, "/", $2, "(" $5 ")"}' 2>/dev/null)"
        MOTD_TOTAL_MEMORY="$(free -h | awk '{print $2}' | sed -n 2p 2>/dev/null)"

        if [ "${MOTD_PUBLIC_IP}" = "${MOTD_GATEWAY_IP}" ]; then
            MOTD_GATEWAY_IP=""
        fi

        [ -z "${MOTD_AVAILABILITY_ZONE}" ] && MOTD_AVAILABILITY_ZONE="${MOTD_DEFAULT_VALUE}"
        [ -z "${MOTD_GATEWAY_IP}" ] && MOTD_GATEWAY_IP="${MOTD_DEFAULT_VALUE}"
        [ -z "${MOTD_HOSTNAME}" ] && MOTD_HOSTNAME="${MOTD_DEFAULT_VALUE}"
        [ -z "${MOTD_INSTANCE_ID}" ] && MOTD_INSTANCE_ID="${MOTD_DEFAULT_VALUE}"
        [ -z "${MOTD_INSTANCE_TYPE}" ] && MOTD_INSTANCE_TYPE="${MOTD_DEFAULT_VALUE}"
        [ -z "${MOTD_OS}" ] && MOTD_OS="${MOTD_DEFAULT_VALUE}"
        [ -z "${MOTD_PRIVATE_IP}" ] && MOTD_PRIVATE_IP="${MOTD_DEFAULT_VALUE}"
        [ -z "${MOTD_PUBLIC_IP}" ] && MOTD_PUBLIC_IP="${MOTD_DEFAULT_VALUE}"
        [ -z "${MOTD_TOTAL_CPUS}" ] && MOTD_TOTAL_CPUS="${MOTD_DEFAULT_VALUE}"
        [ -z "${MOTD_TOTAL_DISKS}" ] && MOTD_TOTAL_DISKS="${MOTD_DEFAULT_VALUE}"
        [ -z "${MOTD_TOTAL_DISK_USED}" ] && MOTD_TOTAL_DISK_USED="${MOTD_DEFAULT_VALUE}"
        [ -z "${MOTD_TOTAL_MEMORY}" ] && MOTD_TOTAL_MEMORY="${MOTD_DEFAULT_VALUE}"
        [ -z "${MOTD_VPC_ID}" ] && MOTD_VPC_ID="${MOTD_DEFAULT_VALUE}"

        printf "$(tput sgr0;tput setaf 124)%-$(tput cols)s$(tput sgr0)\n%-$(tput cols)s\n" \
            "This system is operated and monitored by a private party." ""
        printf "   %sHostname: %s\n" "$(tput bold;tput setaf 216)" "$(tput sgr0;tput setaf 180;echo ${MOTD_HOSTNAME})"
        printf "         %sOS: %-47s  " "$(tput bold;tput setaf 75)" "$(tput sgr0;tput setaf 32;echo ${MOTD_OS})"
        printf "      %sTotal CPUs: %s\n" "$(tput bold;tput setaf 48)" "$(tput sgr0;tput setaf 78;echo ${MOTD_TOTAL_CPUS})"
        printf "  %sPublic IP: %-48s  " "$(tput bold;tput setaf 250)" "$(tput sgr0;tput setaf 246;echo ${MOTD_PUBLIC_IP})"
        printf "    %sTotal Memory: %s\n" "$(tput bold;tput setaf 48)" "$(tput sgr0;tput setaf 78;echo ${MOTD_TOTAL_MEMORY})"
        printf " %sPrivate IP: %-48s  " "$(tput bold;tput setaf 250)" "$(tput sgr0;tput setaf 246;echo ${MOTD_PRIVATE_IP})"
        printf "     %sTotal Disks: %s\n" "$(tput bold;tput setaf 48)" "$(tput sgr0;tput setaf 78;echo ${MOTD_TOTAL_DISKS})"
        printf " %sGateway IP: %-48s  " "$(tput bold;tput setaf 250)" "$(tput sgr0;tput setaf 246;echo ${MOTD_GATEWAY_IP})"
        printf "  %sRoot Vol. Used: %s\n" "$(tput bold;tput setaf 48)" "$(tput sgr0;tput setaf 78;echo ${MOTD_TOTAL_DISK_USED})"
        printf "%sInstance Id: %-48s  " "$(tput bold;tput setaf 250)" "$(tput sgr0;tput setaf 246;echo ${MOTD_INSTANCE_ID})"
        printf "     %sAvail. Zone: %s\n" "$(tput bold;tput setaf 250)" "$(tput sgr0;tput setaf 246;echo ${MOTD_AVAILABILITY_ZONE})"
        printf "     %sVPC Id: %-48s  " "$(tput bold;tput setaf 250)" "$(tput sgr0;tput setaf 246;echo ${MOTD_VPC_ID})"
        printf "   %sInstance Type: %s\n" "$(tput bold;tput setaf 250)" "$(tput sgr0;tput setaf 246;echo ${MOTD_INSTANCE_TYPE})"
        printf "$(tput sgr0)%-$(tput cols)s\n" ""
    }

    show_motd || true
)

请特别注意以下行:

        printf "         %sOS: %-47s  " "$(tput bold;tput setaf 75)" "$(tput sgr0;tput setaf 32;echo ${MOTD_OS})"

%-47s 与其余部分的 %-48s 不同,因为这似乎解决了我正在描述的问题,但是如果我将其恢复到匹配其余部分,因此所有行都独占使用 %-48s,会发生以下问题:

This system is operated and monitored by a private party.

   Hostname: nova.localdomain
         OS: Fedora 24 (Twenty Four)                 Total CPUs: 6
  Public IP: -                                    Total Memory: 7.8G
 Private IP: 192.168.1.100                         Total Disks: 2
 Gateway IP: -                                  Root Vol. Used: 11G / 32G (36%)
Instance Id: -                                     Avail. Zone: -
     VPC Id: -                                   Instance Type: -

请注意 Total CPUs: 单元格如何被向右推一个字符,但在该行上使用 %-47s 解决了这个问题,因此它与该列中的其余单元格。

我想知道是否有人可以向我解释这是为什么以及如何解决这个问题以便所有 printf 行都使用相同的填充值?

就其值(value)而言,我的 bash 版本是:

GNU bash, version 4.3.42(1)-release (x86_64-redhat-linux-gnu)

谢谢!

最佳答案

经过反复试验,我自己解决了这个问题。

此问题是由在 printf 行中调用 tput 创建的丰富格式引起的。由于根据颜色 ID 的字符串长度生成的格式字符串长度不同(例如 246(长度为 3)与 32(长度为 2)),这导致了对齐问题。

上述原文件的补丁如下:

diff --git a/motdA.sh b/motdB.sh
index b73c6ab..fa3e609 100644
--- a/motdA.sh
+++ b/motdB.sh
@@ -42,6 +42,8 @@
         MOTD_OS="$(cat /etc/system-release | sed 's/ release / /g' 2>/dev/null)"
         [ -z "${MOTD_OS}" ] && MOTD_OS="$(cat /etc/os-release | grep 'PRETTY_NAME' | cut -d\" -f2 2>/dev/null)"

+        MOTD_OS_COLOR_B="$(cat /etc/os-release | grep 'ANSI_COLOR' | cut -d\" -f2 2>/dev/null)"
+
         MOTD_HOSTNAME="$(hostnamectl --static 2>/dev/null)"
         if [ -z "${MOTD_HOSTNAME}" ]; then
             MOTD_HOSTNAME="$(hostnamectl --transient 2>/dev/null)"
@@ -70,6 +72,7 @@
         [ -z "${MOTD_HOSTNAME}" ] && MOTD_HOSTNAME="${MOTD_DEFAULT_VALUE}"
         [ -z "${MOTD_INSTANCE_ID}" ] && MOTD_INSTANCE_ID="${MOTD_DEFAULT_VALUE}"
         [ -z "${MOTD_INSTANCE_TYPE}" ] && MOTD_INSTANCE_TYPE="${MOTD_DEFAULT_VALUE}"
+        [ -z "${MOTD_OS_COLOR_B}" ] && MOTD_OS_COLOR_B="0;32"
         [ -z "${MOTD_OS}" ] && MOTD_OS="${MOTD_DEFAULT_VALUE}"
         [ -z "${MOTD_PRIVATE_IP}" ] && MOTD_PRIVATE_IP="${MOTD_DEFAULT_VALUE}"
         [ -z "${MOTD_PUBLIC_IP}" ] && MOTD_PUBLIC_IP="${MOTD_DEFAULT_VALUE}"
@@ -79,20 +82,25 @@
         [ -z "${MOTD_TOTAL_MEMORY}" ] && MOTD_TOTAL_MEMORY="${MOTD_DEFAULT_VALUE}"
         [ -z "${MOTD_VPC_ID}" ] && MOTD_VPC_ID="${MOTD_DEFAULT_VALUE}"

+        MOTD_OS_COLOR_A="1;${MOTD_OS_COLOR_B:2}"
+
+        MOTD_PADDING="48" # Arbitrary length
+        MOTD_PADDING_OS="$((${MOTD_PADDING}-${#MOTD_OS_COLOR_B}-6))" # 6 comes from length of "\033[m"
+
         printf "$(tput sgr0;tput setaf 124)%-$(tput cols)s$(tput sgr0)\n%-$(tput cols)s\n" \
             "This system is operated and monitored by a private party." ""
         printf "   %sHostname: %s\n" "$(tput bold;tput setaf 216)" "$(tput sgr0;tput setaf 180;echo ${MOTD_HOSTNAME})"
-        printf "         %sOS: %-47s  " "$(tput bold;tput setaf 75)" "$(tput sgr0;tput setaf 32;echo ${MOTD_OS})"
+        printf "         %sOS: %-${MOTD_PADDING_OS}s  " "$(echo -en "\033[${MOTD_OS_COLOR_A}m")" "$(echo -en "\033[${MOTD_OS_COLOR_B}m${MOTD_OS}")"
         printf "      %sTotal CPUs: %s\n" "$(tput bold;tput setaf 48)" "$(tput sgr0;tput setaf 78;echo ${MOTD_TOTAL_CPUS})"
-        printf "  %sPublic IP: %-48s  " "$(tput bold;tput setaf 250)" "$(tput sgr0;tput setaf 246;echo ${MOTD_PUBLIC_IP})"
+        printf "  %sPublic IP: %-${MOTD_PADDING}s  " "$(tput bold;tput setaf 250)" "$(tput sgr0;tput setaf 246;echo ${MOTD_PUBLIC_IP})"
         printf "    %sTotal Memory: %s\n" "$(tput bold;tput setaf 48)" "$(tput sgr0;tput setaf 78;echo ${MOTD_TOTAL_MEMORY})"
-        printf " %sPrivate IP: %-48s  " "$(tput bold;tput setaf 250)" "$(tput sgr0;tput setaf 246;echo ${MOTD_PRIVATE_IP})"
+        printf " %sPrivate IP: %-${MOTD_PADDING}s  " "$(tput bold;tput setaf 250)" "$(tput sgr0;tput setaf 246;echo ${MOTD_PRIVATE_IP})"
         printf "     %sTotal Disks: %s\n" "$(tput bold;tput setaf 48)" "$(tput sgr0;tput setaf 78;echo ${MOTD_TOTAL_DISKS})"
-        printf " %sGateway IP: %-48s  " "$(tput bold;tput setaf 250)" "$(tput sgr0;tput setaf 246;echo ${MOTD_GATEWAY_IP})"
+        printf " %sGateway IP: %-${MOTD_PADDING}s  " "$(tput bold;tput setaf 250)" "$(tput sgr0;tput setaf 246;echo ${MOTD_GATEWAY_IP})"
         printf "  %sRoot Vol. Used: %s\n" "$(tput bold;tput setaf 48)" "$(tput sgr0;tput setaf 78;echo ${MOTD_TOTAL_DISK_USED})"
-        printf "%sInstance Id: %-48s  " "$(tput bold;tput setaf 250)" "$(tput sgr0;tput setaf 246;echo ${MOTD_INSTANCE_ID})"
+        printf "%sInstance Id: %-${MOTD_PADDING}s  " "$(tput bold;tput setaf 250)" "$(tput sgr0;tput setaf 246;echo ${MOTD_INSTANCE_ID})"
         printf "     %sAvail. Zone: %s\n" "$(tput bold;tput setaf 250)" "$(tput sgr0;tput setaf 246;echo ${MOTD_AVAILABILITY_ZONE})"
-        printf "     %sVPC Id: %-48s  " "$(tput bold;tput setaf 250)" "$(tput sgr0;tput setaf 246;echo ${MOTD_VPC_ID})"
+        printf "     %sVPC Id: %-${MOTD_PADDING}s  " "$(tput bold;tput setaf 250)" "$(tput sgr0;tput setaf 246;echo ${MOTD_VPC_ID})"
         printf "   %sInstance Type: %s\n" "$(tput bold;tput setaf 250)" "$(tput sgr0;tput setaf 246;echo ${MOTD_INSTANCE_TYPE})"
         printf "$(tput sgr0)%-$(tput cols)s\n" ""
     }

感谢所有做出贡献的人!

关于string - 内置 printf bash 的可能填充错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39214425/

相关文章:

html - 当我最小化我的 HTML/CSS 页面时,它会压缩所有图片和文本并破坏格式

html - 如何将自定义 HTML 放入 Yii2 GridView header 中?

javascript - 删除字符串中所有出现的文本

linux - 在 Cron 中连接失败后重新运行 Rsync

java - 如何找到字符串中的最后一个字符?

linux - 仅当行号以 + csv 文件开头时才替换行中的单词

linux - 输出重定向应该重新创建目标文件

Javascript:如何将时间(存储为分数)转换为可读字符串?

c - 从用户那里获取一个字符串作为单个命令行参数,标记并显示它

c++ - 检查字符