如何在 Bash 中对齐并居中文本?

如何在 Bash 中对齐并居中文本?

我是新手。我正在寻找用于居中和对齐文本的 bash 脚本。我的脚本仅适用于一行文本。您如何改进它?

#!/bin/bash
COLS=$(tput cols)
while true; do
    clear
    echo -n "Type text "
    read text
    echo
    echo "Menu"
    echo "1) Right justify  "
    echo "2) Center "
    echo "3) Exit "
    echo
    echo -n "Choose [1-3]: "
    read opt
    echo
    case $opt in
        1) printf "%*s\n" $COLS "$text"
           break
        ;;
        2) printf "%*s\n" $[$COLS/2] "$text"
           break
        ;;      
        3) break
        ;;
        *)
        echo "Error. Press [1-3]"
        break
        ;;
    esac
done

答案1

正如您所发现的,$COLUMNS仅在交互式 shell 中有用-i,因此我们使用columns="$(tput cols)"

我唯一遇到的问题是下面这一行。它没有将文本居中。
printf "%*s\n" $[$COLS/2] "$text"

扩展您的工作,这里有一个显示居中文本的函数(来自文件)。要在脚本中调用它,请使用display_center "file.txt"

display_center(){
    columns="$(tput cols)"
    while IFS= read -r line; do
        printf "%*s\n" $(( (${#line} + columns) / 2)) "$line"
    done < "$1"
}

请注意使用${#line}(类似于wc -m)来计算行中的字符数。只要您只需要显示纯文本而不显示颜色/格式,那么这应该可以正常工作。

这是一个使用与 printf 相同的实现来显示右对齐文本(来自文件)的函数。

display_right(){
    columns="$(tput cols)"
    while IFS= read -r line; do
        printf "%*s\n" $columns "$line"
    done < "$1"
}

您也可以使用 tput 和 echo 做类似的事情,但下面的示例不是那么强大(即,长字符串将会失败)。

row=0
col=$(( ($(tput cols) - ${#text}) / 2))
tput clear
tput cup $row $col
echo "$text"

另外,您可能要考虑使用dialogselect来生成菜单。这将使您的脚本更加简洁。
http://bash.cyberciti.biz/guide/Select_loop
https://serverfault.com/questions/144939/multi-select-menu-in-bash-script

答案2

#!/usr/bin/awk -f
{
  z = 92 - length
  y = int(z / 2)
  x = z - y
  printf "%*s%s%*s\n", x, "", $0, y, ""
}

输入

你好世界
阿尔法 布拉沃 查理 德尔塔

输出

                                         你好世界
                                  阿尔法 布拉沃 查理 德尔塔

答案3

我已经玩脚本一段时间了。我是一名木匠和机械师,所以请耐心听我说,哈哈。我知道这是一个老问题,但我最近在寻找类似的东西来美化我一直在研究的过于复杂的 Arch 安装脚本,但从未找到符合我需求的答案。所以我写了一个(可能有点黑客)函数,它提供了一些选项来简化我正在寻找的格式。我想如果有人感兴趣的话我会分享一下。这一切都是在 zsh 中完成的。

# Formatting using printf
# 
# _L == total length for 'L' and 'R' options
# tl == total length for 'C' option
# _c == center for 'C' option ( tl / 2 )
# fill == filler character or space by default
# 
# Default lengths declared at top of function
# 
# usage $) f_M [string] [L,R,C] [character count]
#       -S option:
#       $) f_M -S [character] [total length (- for default 'space')]
#        -3rd var ($3) is needed for filler character ($2) or
#         else ($2) is total length
# 
# 

f_M () {
    tl=72
    _c=36
    _L=16
    f_numread () {
        printf $1 | sed ':a;s/\B[0-9]\{3\}\>/,&/;ta'
    }
    f_C () {
        local x=$(printf $1 | wc -c)
        local y=$(expr $_c - $(expr $x / 2))
        local z=$(expr $tl - $y)
        local space=' '
        printf "%${y}s%-${z}s\n" "$space" "$1"
    }
    case $1 in
        -S)
            fill=' '
            if [[ -n $2 ]]; then
                if [[ -n $3 ]]; then
                    fill="$2"
                    if [[ $3 =~ ^-?[0-9]+$ ]]; then
                        tl=$3
                    fi
                else
                    if [[ $2 =~ ^-?[0-9]+$ ]]; then
                        tl=$2
                    fi
                fi
            fi
            printf "${fill}%.0s" {1..$tl}
            ;;
        *)
            if [[ $3 =~ ^-?[0-9]+$ ]]; then
                tl=$3
                _L=$3
                _c=$(expr $tl / 2)
            fi
            if [[ $1 =~ ^-?[0-9]+$ ]]; then
                local x=$(f_numread $1)
                case $2 in
                    L)
                        printf "%-${_L}s\n" "$x" ;;
                    R)
                        printf "%${_L}s\n" "$x" ;;
                    C)
                        f_C $x ;;
                    "")
                        printf '%s\n' $x
                esac
            else
                case $2 in
                    L)
                        printf "%-${_L}s\n" "$1" ;;
                    R)
                        printf "%${_L}s\n" "$1" ;;
                    C)
                        f_C $1 ;;
                    "")
                        printf '%s\n' $1
                esac
            fi
            ;;
    esac
}

以下是我使用它的一些示例...

# Default character length is 72

[user@arch]$ printf '%s\n' "<$(f_M -S)>"
<                                                                        >

[user@arch]$ printf '%s\n' "<$(f_M -S = 50)>"
<==================================================>

# To change the 'fill' character(s), a 3rd argument is required
# So I set '-' to keep my default value
# There is probably a better way to do it but this fit my needs

[user@arch]$ printf '%s\n' "<$(f_M -S = -)>"
<========================================================================>

# Because of what I was trying to do and to get multiple uses out of
# one function, I use separate variables for 'C' than for 'L' and 'R'

[user@arch]$ printf '%s\n%s\n%s\n' "<$(f_M 'left' L 72)>" \
    "<$(f_M 'center' C)>" "<$(f_M 'right' R 72)>"
<left                                                                    >
<                                 center                                 >
<                                                                   right>

# It will also take a string that is only numbers and make
# it more "human readable"

[user@arch]$ num=12345678
[user@arch]$ printf '%s\n' "<$(f_M ${num} C 50)>"
<                    12,345,678                    >

为了更具创造力,我可以添加另一个函数,该函数将使用数组自动创建一个漂亮的文本框……

#!/bin/zsh

f_fancy_box () {
    local s=''
    for s in ${text[@]}; do
        case $s in
            000) printf '%b\n' "<$(f_M -S = -)>" ;;
            ---) printf '%b\n' "| $(f_M -S - 70) |" ;;
            *) printf '%b\n' "|$(f_M $s C)|" ;;
        esac
    done
}

text=(
"000"
" "
"It is hobbies like this"
"that keep me from ever"
"getting any sleep lol"
" "
"---"
" "
"But I think"
"it is worth it"
":P"
" "
"000"
)

f_fancy_box

exit

输出:

<========================================================================>
|                                                                        |
|                         It is hobbies like this                        |
|                         that keep me from ever                         |
|                          getting any sleep lol                         |
|                                                                        |
| ---------------------------------------------------------------------- |
|                                                                        |
|                               But I think                              |
|                             it is worth it                             |
|                                   :P                                   |
|                                                                        |
<========================================================================>

我希望有人觉得这有用!

编辑:将 f_fancy_box 中的 printf '%s' 更改为 '%b'。根据 printf 的手册页,为了允许字符转义,我认为这实际上是合适的命令。如果我错了,请纠正我...

答案4

A功能接收文本并将其水平居中,大批也可以用来输入文本。

它的工作原理如下,细绳传递给函数并保存在数组中,然后使用for 循环遍历数组,并在终端中心打印每一行。

function centerText() {
    columns="$(tput cols)"
    declare -a text=()
    text+=($*)
    for((i=0;i<${#text[@]};i++)); do
        printf "%*s\n" $(( (${#text[i]} + columns) / 2)) "${text[i]}"
    done
}

以下是一个例子:

function centerText() {
    columns="$(tput cols)"
    declare -a text=()
    text+=($*)
    for((i=0;i<${#text[@]};i++)); do
        printf "%*s\n" $(( (${#text[i]} + columns) / 2)) "${text[i]}"
    done
}

declare -a text=(
'First'
'Second'
'Third'
)

centerText ${text[@]}
sleep 5

输出:

                                     First
                                     Second
                                     Third



PD:如果有表述错误的话,请见谅,我的母语不是英语。

相关内容