简体   繁体   English

内置printf bash可能出现填充错误

[英]Possible padding bug with printf bash built-in

I have been having a bit of trouble with an "off-by-one" issue while using the printf bash built-in to pad a string to a specific width. 在使用内置的printf bash将字符串填充到特定宽度时,我遇到了一个“off-by-one”问题。

Take the following code: 请使用以下代码:

#!/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
)

Note the following line specifically: 请注意以下行:

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

The %-47s is different from the %-48s that the rest has because this seems to resolve the issue I'm describing, however if I restore it to match the rest so all of the lines use %-48s exclusively, the following problem happens: %-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: -

Note how the Total CPUs: cell is pushed to the right by one character, yet using %-47s on that line resolves the issue so that it's flush with the rest of the cells in that column. 请注意Total CPUs:单元格是如何向右推动一个字符,但在该行上使用%-47s可以解决问题,使其与该列中的其余单元格齐平。

I am wondering if anyone can explain to me why this is and how to resolve the issue so all of the printf lines use the same padding value? 我想知道是否有人可以向我解释为什么这是以及如何解决问题所以所有printf行使用相同的填充值?

For what it's worth, my bash version is: 对于它的价值,我的bash版本是:

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

Thanks! 谢谢!

I fixed this issue myself after trial and error. 经过反复试验,我自己解决了这个问题。

The issue was caused by the rich formatting created by calling tput in the printf lines. 问题是由在printf行中调用tput创建的丰富格式引起的。 This was causing alignment issues due to differing lengths of formatting strings that were being generated based on the string length of the color id (eg 246 (length of 3) vs. 32 (length of 2)). 由于基于颜色id的字符串长度(例如,246(长度为3)对32(长度为2))生成的格式化字符串的长度不同,这导致对齐问题。

The patch to the original file above is below: 上面原始文件的补丁如下:

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" ""
     }

Thanks everyone who contributed! 谢谢所有贡献的人!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM