Bash 脚本将财富/文本从标准输入/管道居中

Iac*_*hus 5 bash

我在 python3 中使用一个小脚本在控制台中显示居中的命运,你能建议我如何在纯 bash 中做到这一点吗?

文件:center.python3

#!/usr/bin/env python3

import sys, os

linelist = list(sys.stdin)

# gets the biggest line
biggest_line_size = 0
for line in linelist:
    line_lenght = len(line.expandtabs())
    if line_lenght > biggest_line_size:
        biggest_line_size = line_lenght

columns = int(os.popen('tput cols', 'r').read())
offset = biggest_line_size / 2
perfect_center = columns / 2
padsize =  int(perfect_center - offset)
spacing = ' ' * padsize # space char

text = str()
for line in linelist:
    text += (spacing + line)

divider = spacing + ('?' * int(biggest_line_size)) # unicode 0x2500
text += divider

print(text, end="\n"*2)
Run Code Online (Sandbox Code Playgroud)

然后在 .bashrc

使其可执行后chmod +x ~/center.python3

fortune | ~/center.python3
Run Code Online (Sandbox Code Playgroud)

编辑:稍后我会尝试根据我的评论回复这个 OP,但现在我让它更有文化。

编辑 2:更新 python 脚本以解决@janos 指出的关于选项卡扩展的错误。

在此处输入图片说明

jan*_*nos 7

让我们逐块从 Python 转换为 Bash。

Python:

#!/usr/bin/env python3

import sys, os

linelist = list(sys.stdin)
Run Code Online (Sandbox Code Playgroud)

重击:

#!/usr/bin/env bash

linelist=()
while IFS= read -r line; do
    linelist+=("$line")
done
Run Code Online (Sandbox Code Playgroud)

Python:

# gets the biggest line
biggest_line_size = 0
for line in linelist:
    line_lenght = len(line)
    if line_lenght > biggest_line_size:
        biggest_line_size = line_lenght
Run Code Online (Sandbox Code Playgroud)

重击:

biggest_line_size=0
for line in "${linelist[@]}"; do
    # caveat alert: the length of a tab character is 1
    line_length=${#line}
    if ((line_length > biggest_line_size)); then
        biggest_line_size=$line_length
    fi
done
Run Code Online (Sandbox Code Playgroud)

Python:

columns = int(os.popen('tput cols', 'r').read())
offset = biggest_line_size / 2
perfect_center = columns / 2
padsize =  int(perfect_center - offset)
spacing = ' ' * padsize # space char
Run Code Online (Sandbox Code Playgroud)

重击:

columns=$(tput cols)
# caveat alert: division truncates to integer value in Bash
((offset = biggest_line_size / 2))
((perfect_center = columns / 2))
((padsize = perfect_center - offset))
if ((padsize > 0)); then
    spacing=$(printf "%*s" $padsize "")
else
    spacing=
fi
Run Code Online (Sandbox Code Playgroud)

Python:

text = str()
for line in linelist:
    text += (spacing + line)

divider = spacing + ('?' * int(biggest_line_size)) # unicode 0x2500
text += divider

print(text, end="\n"*2)
Run Code Online (Sandbox Code Playgroud)

重击:

for line in "${linelist[@]}"; do
    echo "$spacing$line"
done

printf $spacing 
for ((i = 0; i < biggest_line_size; i++)); do
    printf -- -
done
echo
Run Code Online (Sandbox Code Playgroud)

更容易复制粘贴的完整脚本:

#!/usr/bin/env bash

linelist=()
while IFS= read -r line; do
    linelist+=("$line")
done

biggest_line_size=0
for line in "${linelist[@]}"; do
    line_length=${#line}
    if ((line_length > biggest_line_size)); then
        biggest_line_size=$line_length
    fi
done

columns=$(tput cols)
((offset = biggest_line_size / 2))
((perfect_center = columns / 2))
((padsize = perfect_center - offset))
spacing=$(printf "%*s" $padsize "")

for line in "${linelist[@]}"; do
    echo "$spacing$line"
done

printf "$spacing"
for ((i = 0; i < biggest_line_size; i++)); do
    printf ?  # unicode 0x2500
done
echo
Run Code Online (Sandbox Code Playgroud)

注意事项摘要

Bash 中的除法会截断。所以offset,perfect_center和的值padsize可能略有不同。

原始 Python 代码中也存在一些问题:

  1. 制表符的长度为 1。这会导致有时分隔线看起来比最长的线短,如下所示:

                      Q:    Why did the tachyon cross the road?
                      A:    Because it was on the other side.
                      ??????????????????????????????????????
    
    Run Code Online (Sandbox Code Playgroud)
  2. 如果某些线长于columns,分隔线可能会更好地使用 的长度columns而不是最长的线。

  • 您可以使用 [`expand`](http://man7.org/linux/man-pages/man1/expand.1.html) 修复问题 1。例如,`linelist+=($(echo "$line" | expand))`。 (2认同)

xhi*_*nne 3

这是我的脚本center.sh

\n\n
#!/bin/bash\n\nreadarray message < <(expand)\n\nwidth="${1:-$(tput cols)}"\n\nmargin=$(awk -v "width=$width" \'\n    { max_len = length > width ? width : length > max_len ? length : max_len }\n    END { printf "%" int((width - max_len + 1) / 2) "s", "" }\n\' <<< "${message[@]}")\n\nprintf "%s" "${message[@]/#/$margin}"\n
Run Code Online (Sandbox Code Playgroud)\n\n

怎么运行的:

\n\n
    \n
  • 第一个命令将制表转换为空格后将每一行放入stdin数组中message(感谢@NominalAnimal)
  • \n
  • 第二个命令从参数 #1 读取窗口宽度并将其放入变量中width。如果没有给出参数,则使用实际的终端宽度。
  • \n
  • 第三个命令将整体发送到messageawk以便将左边距作为空格字符串生成,并将其放入变量 中margin。\n\n
      \n
    • 对每个输入行执行第一行 awk 。它计算max_len,最长输入行的长度(上限为width
    • \n
    • 当所有输入行都处理完毕后,执行第二个 awk 行。它打印一串(width - max_len) / 2空白字符
    • \n
  • \n
  • 最后一个命令打印添加到它们message之后的每一行margin
  • \n
\n\n

测试 :

\n\n
$ fortune | cowthink | center.sh\n                    _______________________________________\n                   ( English literature\'s performing flea. )\n                   (                                       )\n                   ( -- Sean O\'Casey on P. G. Wodehouse    )\n                    ---------------------------------------\n                           o   ^__^\n                            o  (oo)\\_______\n                               (__)\\       )\\/\\\n                                   ||----w |\n                                   ||     ||\n\n$ echo $\'|\\tTAB\\t|\' | center.sh 20\n  |       TAB     |\n\n$ echo "A line exceeding the maximum width" | center.sh 10\nA line exceeding the maximum width\n
Run Code Online (Sandbox Code Playgroud)\n\n

最后,如果您想以分隔线结束显示(就像在 Python 脚本中一样),请在最后一个printf命令之前添加此行:

\n\n
message+=( $(IFS=\'\'; sed s/./\xe2\x94\x80/g <<< "${message[*]}" | sort | tail -n1)$\'\\n\' )\n
Run Code Online (Sandbox Code Playgroud)\n\n

它的作用是将每行中的每个字符替换为\xe2\x94\x80,选择最长的字符sort | tail -n1,并将其添加到消息末尾。

\n\n

测试:

\n\n
$ fortune | center.sh  60\n     Tuesday is the Wednesday of the rest of your life.\n     \xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\n
Run Code Online (Sandbox Code Playgroud)\n