三 控制台的对齐输出

三 控制台的对齐输出

【动机】

我们在执行大量 linux shell 脚本或命令时, 常常少不了超前打印一些提示或表示正在执行之类的文本, 为避免单调, 有时候需要将文本做一些右对齐或居中显示, 而如果不指定位置, echo / printf 默认只是在当前位置顺序打印.

有了第二节的字符串占宽, 对齐输出不再是难事.

【意图】

指定输出命令或函数, 包括 echo 和第一节的颜色文本函数; 指定左对齐(默认), 居中或右对齐; 在当前行打印指定字符串

【实现】

注意到指定命令/函数, 以及指定对齐方式(左对齐 / 居中 / 有对齐), 均需要包含输入字符串的防误操作及默认处理, 故在 data.sh 中, 新建函数 available_range, 以供后续调用:

:<<COMMENT
    尽量返回期待值, 如果期待值不在指定范围则返回默认值 
    $1 : 期待值
    $2 : 默认值
    $3 及以后: 允许范围值
    使用: available_range <expected> <default> <list>
        其中 list 与 index_in_strings 相同
    返回值: 优先考虑期待数据, 不合格则返回默认值
COMMENT
available_range(){
    local expect="$1" defv="$2" 
    shift 2
    [ $(index_in_strings "$expect" $@) == -1 ] && echo $defv || echo $expect
}

函数比较简单, 不做过多解释(其中调用的 index_in_strings, 在花式清屏中有论述). 然后是在 console.sh 中做好可用的输出函数的声明:

# 除了 echo, 其他可用的输出方式
__AVAI_OP__=(
    caption 
    menu
    notice
    tip
    success
    info
    warn
    error
    critical
    )

下面是 console.sh 中的 aop 函数( align output 之意):

:<<COMMENT
    aop, 即 align output, 屏幕对齐打印
    $1: comdline, 输出要使用的命令 / 函数, 可以带 echo 接受的选项: -n -e, 
        但要求带选项时, 必须用引号包围起来. 例如 'info -ne' , 'success -e' 等等. 
        默认为 echo, 其他可用值参见 __AVAI_OP__       
    $2: align 对齐方式, 默认为 left. 其他可用值为: center right
    $3 及以后: str 要输出的字符串
    用法 aop <func=echo?|warn|info|success|menu|critical|error|caption...> <align=left?> <str1> <str2>...
    注意: 支持在文本中重新指定颜色, 此处颜色优先于指定的函数的自带颜色.
COMMENT
aop(){

    # 允许打印方法带原生 echo 可用的选项, 例如 -n -e , 所以, 需要先剥离出方法名
    local arr_cmdline
    read -ra  arr_cmdline <<<"$1"

    local func=$(available_range "${arr_cmdline[0]}" 'echo' $(echo ${__AVAI_OP__[@]}))

    # 选项: 即使长度为 1, 或甚至 0, 也不报错
    local opts=${arr_cmdline[@]:1:$[${#arr_cmdline[@]}-1]}  

    # 对齐方式
    local align=$(available_range "$2" left right left center)

    shift 2
    # 如果文本中重新指定了颜色, 则计算文本总占列, 应去除这些表示颜色的标识
    local valids=$(echo "$@" | sed -r 's/(\\e|\\033)\[[0-9;]+m//g')
    local w=$(cols_in_screen "$valids")
    log_critical "<$@>, width:$w"
    local cols=$(tput cols)
    local rest=$[cols-w]

    # 如果字符串太长以至于大于屏幕列数, 则指定的对齐是没有意义的.
    if [[ $rest -gt 1 && ( $align == center || $align == right ) ]]; then
        [ $align == center ] && rest=$[rest/2]
        # 通过打印若干空格后再打印文本的方式实现对齐. \r: 保证对齐从屏幕第一列算起
        printf "\r%${rest}s"
    fi

    $func $opts "$@"
}

【测试】

新建 test.sh

    # 根据实际文件位置修改路径
    source ./system.sh
    source ./data.sh
    source ./console.sh
    way1(){
        local strs=("If you want to continue using the Docker Hub API"
            "中文 mixin english 以及罗马字母 γδεζη"
            "有网民使用AI生成功能制造...")
        local aln cmd
        for cmd in tip success; do
            for aln in left center right; do
                for str in "${strs[@]}"; do
                    aop $cmd $aln $str
                done
            done
        done
        aop info center "一般常用居中格式, 最后加入换行"
        aop 'notice -n' center "居中, 但末尾不换行"
        echo "........结束行"
    }
    way1

运行结果如下:

【另类】

下面尝试在文本中重新指定颜色:

    # 根据实际文件位置修改路径
    source ./system.sh
    source ./data.sh
    source ./console.sh   
    way2(){
        echo 
        local core_str="这是一段好长好长的居中文本 testing text..."
        local str1="\e[31;44;5m$core_str\e[0m"
        local str2="\033[35;42;1m$core_str\e[0m"
        aop 'info -e' center "$core_str"
        aop 'info -e' center "$str1"  
        aop 'info -e' center "$str2"  
    }
    way2

输出结果:

可见, 颜色被重新指定, 居中属性也未受影响.

谢谢观看!