私はコンソールに集中した富を表示するために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)
それから.bashrc
実行可能にした後chmod +x ~/center.python3
:
fortune | ~/center.python3
編集する:後で私の意見に基づいてこのOPに返信しますが、今はもう少し読みやすくしています。
編集2: タブ拡張に関して @janos が指摘したバグを修正するために Python スクリプトを更新しました。
答え1
PythonからBashに1つずつ変換してみましょう。
Python:
#!/usr/bin/env python3 import sys, os linelist = list(sys.stdin)
大きな打撃:
#!/usr/bin/env bash
linelist=()
while IFS= read -r line; do
linelist+=("$line")
done
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
大きな打撃:
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
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
大きな打撃:
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
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)
大きな打撃:
for line in "${linelist[@]}"; do
echo "$spacing$line"
done
printf $spacing
for ((i = 0; i < biggest_line_size; i++)); do
printf -- -
done
echo
より簡単なコピー - 貼り付けのための完全なスクリプト:
#!/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
考慮事項の概要
Bashの分割が切り捨てられます。したがってoffset
、perfect_center
、 の値がpadsize
少しずつ異なる場合があります。
元のPythonコードにはいくつかの問題があります。
タブ文字の長さは1です。これにより、次のように、区切り線が最も長い線より短くなることがあります。
Q: Why did the tachyon cross the road? A: Because it was on the other side. ──────────────────────────────────────
一部の行が区切り文字の長さより長い場合は、長さの代わりに最長の行を
columns
使用する方が良い場合があります。columns
答え2
これは私のスクリプトですcenter.sh
。
#!/bin/bash
readarray message < <(expand)
width="${1:-$(tput cols)}"
margin=$(awk -v "width=$width" '
{ max_len = length > width ? width : length > max_len ? length : max_len }
END { printf "%" int((width - max_len + 1) / 2) "s", "" }
' <<< "${message[@]}")
printf "%s" "${message[@]/#/$margin}"
仕組み:
- 最初のコマンドはタブを空白に変換し、各行を
stdin
配列に入れますmessage
(@NominalAnimalに感謝します)。 - 2番目のコマンドは、パラメータ#1からウィンドウの幅を読み取り、それを変数に入れます
width
。引数が指定されない場合、実際の端末幅が使用されます。 - 3番目のコマンドは、左マージンが空白文字列として生成され、変数に配置されるように
message
全体を送信します。awk
margin
- 各入力ラインに対して awk の最初のラインを実行します。最長入力ラインの長さを計算します
max_len
。 (上限はwidth
) - すべての入力ラインが処理されると、2番目のawkラインが実行されます。
(width - max_len) / 2
空白文字文字列を印刷します。
- 各入力ラインに対して awk の最初のラインを実行します。最長入力ラインの長さを計算します
- 最後のコマンドは、その後
message
に追加された各行を印刷します。margin
テスト:
$ fortune | cowthink | center.sh
_______________________________________
( English literature's performing flea. )
( )
( -- Sean O'Casey on P. G. Wodehouse )
---------------------------------------
o ^__^
o (oo)\_______
(__)\ )\/\
||----w |
|| ||
$ echo $'|\tTAB\t|' | center.sh 20
| TAB |
$ echo "A line exceeding the maximum width" | center.sh 10
A line exceeding the maximum width
最後に(Pythonスクリプトと同様に)区切り線でマークを終了するには、最後のprintf
コマンドの前に次の行を追加します。
message+=( $(IFS=''; sed s/./─/g <<< "${message[*]}" | sort | tail -n1)$'\n' )
これが行うことは、各行のすべての文字をに置き換えて─
、最も長い文字を選択してsort | tail -n1
メッセージの末尾に追加することです。
テスト:
$ fortune | center.sh 60
Tuesday is the Wednesday of the rest of your life.
──────────────────────────────────────────────────
答え3
#!/usr/bin/env bash
# Reads stdin and writes it to stdout centred.
#
# 1. Send stdin to a temporary file while keeping track of the maximum
# line length that occurs in the input. Tabs are expanded to eight
# spaces.
# 2. When stdin is fully consumed, display the contents of the temporary
# file, padded on the left with the apropriate number of spaces to
# make the whole contents centred.
#
# Usage:
#
# center [-c N] [-t dir] <data
#
# Options:
#
# -c N Assume a window width of N columns.
# Defaults to the value of COLUMNS, or 80 if COLUMNS is not set.
#
# -t dir Use dir for temporary storage.
# Defaults to the value of TMPDIR, or "/tmp" if TMPDIR is not set.
tmpdir="${TMPDIR:-/tmp}"
cols="${COLUMNS:-80}"
while getopts 'c:t:' opt; do
case "$opt" in
c) cols="$OPTARG" ;;
t) tmpdir="$OPTARG" ;;
esac
done
tmpfile="$tmpdir/center-$$.tmp"
trap 'rm -f "$tmpfile"' EXIT
while IFS= read -r line
do
line="${line//$'\t'/ }"
len="${#line}"
maxlen="$(( maxlen < len ? len : maxlen ))"
printf '%s\n' "$line"
done >"$tmpfile"
padlen="$(( maxlen < cols ? (cols - maxlen) / 2 : 0 ))"
padding="$( printf '%*s' "$padlen" "" )"
while IFS= read -r line
do
printf '%s%s\n' "$padding" "$line"
done <"$tmpfile"
テスト:
$ fortune | cowsay | ./center
________________________________________
/ "There are two ways of constructing a \
| software design: One way is to make it |
| so simple that there are obviously no |
| deficiencies, and the other way is to |
| make it so complicated that there are |
| no obvious deficiencies." |
| |
\ -- C. A. R. Hoare /
----------------------------------------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
$ fortune | cowsay -f bunny -W 15 | ./center -c 100
_______________
/ It has just \
| been |
| discovered |
| that research |
| causes cancer |
\ in rats. /
---------------
\
\ \
\ /\
( )
.( o ).
答え4
個人的に私は純粋なBashソリューションを追求せずにtput
むしろexpand
。しかし、純粋なBashソリューションはかなり実現可能です。
#!/bin/bash
# Bash should populate LINES and COLUMNS
shopt -s checkwinsize
# LINES and COLUMNS are updated after each external command is executed.
# To ensure they are populated right now, we run an external command here.
# Because we don't want any other dependencies other than bash,
# we run bash. (In that child shell, run the 'true' built-in.)
bash -c true
# Tab character.
tab=$'\t'
# Timeout in seconds, for reading each input line.
timeout=5.0
# Read input lines into lines array:
lines=()
maxlen=0
while read -t $timeout LINE ; do
# Expand each tab in LINE:
while [ "${LINE#*$tab}" != "$LINE" ]; do
# Beginning of LINE, replacing the tab with eight spaces
prefix="${LINE%%$tab*} "
# Length of prefix
length=${#prefix}
# Round length down to nearest multiple of 8
length=$[$length - ($length & 7)]
# Combine prefix and the rest of the line
LINE="${prefix:0:$length}${LINE#*$tab}"
done
# If LINE is longest thus far, update maxlen
[ ${#LINE} -gt $maxlen ] && maxlen=${#LINE}
# Add LINE to lines array.
lines+=("$LINE")
done
# If the output is redirected to a file, COLUMNS will be undefined.
# So, use the following idiom to ensure we have an integer 'cols'.
cols=$[ $COLUMNS -0 ]
# Indentation needed to center the block
if [ $maxlen -lt $cols ]; then
indent=$(printf '%*s' $[($cols-$maxlen)/2] '')
else
indent=""
fi
# Display
for LINE in "${lines[@]}"; do
printf '%s%s\n' "$indent" "$LINE"
done
上記のスクリプトは標準入力から行を読み取り、最長行が端末の中央に来るように出力をインデントします。 Bashが端末の幅を知らないと、正常に失敗します(インデントなし)。
私は以前のバージョンのBash(そしてコンパイル時に新しいスタイル演算子を無効にしたカスタムコンパイルされた最小Bash)との間の最大の互換性が欲しいので、古いスタイル条件演算子([ ... ]
)とシェル算術()を使用しています。$[..]
私は一般的にこれをお勧めしませんが、この場合は純粋なBashソリューションに取り組んでいますので、Bashコンパイルオプション間の最大互換性が推奨コーディングスタイルよりも重要であると思います。