나는 콘솔에 집중된 부를 표시하기 위해 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로 하나씩 변환해 보겠습니다.
파이썬:
#!/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
파이썬:
# 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
파이썬:
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
파이썬:
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에게 감사드립니다). - 두 번째 명령은 매개변수 #1에서 창 너비를 읽고 이를 변수에 넣습니다
width
. 인수가 지정되지 않으면 실제 터미널 너비가 사용됩니다. - 세 번째 명령은 왼쪽 여백이 공백 문자열로 생성되어 변수에 배치되도록
message
전체 를 보냅니다 .awk
margin
- 각 입력 라인에 대해 awk의 첫 번째 라인을 실행합니다. 가장 긴 입력 라인의 길이를 계산합니다
max_len
. (상한은width
) - 모든 입력 라인이 처리되면 두 번째 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 컴파일 옵션 간의 최대 호환성이 권장 코딩 스타일보다 더 중요하다고 생각합니다.